Merge branch 'master' of github.com:grpc/grpc into str_join_with_sep

pull/2319/head
David Garcia Quintas 10 years ago
commit 211e65be3c
  1. 6
      .gitignore
  2. 3
      .travis.yml
  3. 19
      BUILD
  4. 262
      Makefile
  5. 74
      build.json
  6. 57
      doc/connection-backoff.md
  7. 49
      doc/interop-test-descriptions.md
  8. 7
      gRPC.podspec
  9. 62
      include/grpc++/auth_context.h
  10. 4
      include/grpc++/client_context.h
  11. 6
      include/grpc++/credentials.h
  12. 141
      include/grpc++/impl/call.h
  13. 4
      include/grpc++/server.h
  14. 33
      include/grpc++/server_builder.h
  15. 7
      include/grpc++/server_context.h
  16. 26
      include/grpc++/stream.h
  17. 3
      include/grpc/grpc.h
  18. 22
      include/grpc/grpc_security.h
  19. 2
      include/grpc/support/port_platform.h
  20. 22
      include/grpc/support/time.h
  21. 76
      src/compiler/objective_c_generator.cc
  22. 10
      src/core/channel/census_filter.c
  23. 18
      src/core/channel/http_client_filter.c
  24. 30
      src/core/channel/http_server_filter.c
  25. 4
      src/core/client_config/subchannel.c
  26. 4
      src/core/httpcli/httpcli.h
  27. 1
      src/core/iomgr/iocp_windows.c
  28. 39
      src/core/iomgr/iomgr.c
  29. 3
      src/core/iomgr/pollset_multipoller_with_epoll.c
  30. 9
      src/core/iomgr/pollset_posix.c
  31. 6
      src/core/iomgr/pollset_windows.c
  32. 35
      src/core/iomgr/socket_windows.c
  33. 10
      src/core/iomgr/tcp_client_posix.c
  34. 3
      src/core/iomgr/tcp_client_windows.c
  35. 80
      src/core/iomgr/tcp_server_windows.c
  36. 24
      src/core/iomgr/tcp_windows.c
  37. 2
      src/core/profiling/timers_preciseclock.h
  38. 38
      src/core/security/client_auth_filter.c
  39. 169
      src/core/security/credentials.c
  40. 112
      src/core/security/credentials.h
  41. 89
      src/core/security/google_default_credentials.c
  42. 54
      src/core/security/json_token.c
  43. 15
      src/core/security/json_token.h
  44. 832
      src/core/security/jwt_verifier.c
  45. 136
      src/core/security/jwt_verifier.h
  46. 14
      src/core/security/security_context.c
  47. 8
      src/core/security/security_context.h
  48. 4
      src/core/statistics/census_rpc_stats.c
  49. 16
      src/core/statistics/census_tracing.c
  50. 6
      src/core/statistics/window_stats.h
  51. 4
      src/core/support/cancellable.c
  52. 2
      src/core/support/log_linux.c
  53. 2
      src/core/support/log_posix.c
  54. 2
      src/core/support/log_win32.c
  55. 130
      src/core/support/stack_lockfree.c
  56. 50
      src/core/support/stack_lockfree.h
  57. 2
      src/core/support/sync_win32.c
  58. 44
      src/core/support/time_posix.c
  59. 35
      src/core/support/time_win32.c
  60. 80
      src/core/surface/call.c
  61. 35
      src/core/surface/channel.c
  62. 232
      src/core/surface/completion_queue.c
  63. 18
      src/core/surface/completion_queue.h
  64. 2
      src/core/surface/init.c
  65. 281
      src/core/surface/server.c
  66. 41
      src/core/surface/version.c
  67. 9
      src/core/transport/chttp2/frame_data.c
  68. 12
      src/core/transport/chttp2/hpack_parser.c
  69. 6
      src/core/transport/chttp2/hpack_table.c
  70. 2
      src/core/transport/chttp2/incoming_metadata.c
  71. 9
      src/core/transport/chttp2/parsing.c
  72. 29
      src/core/transport/chttp2/stream_encoder.c
  73. 10
      src/core/transport/chttp2_transport.c
  74. 127
      src/core/transport/metadata.c
  75. 26
      src/core/transport/metadata.h
  76. 6
      src/core/transport/stream_op.c
  77. 2
      src/core/transport/transport.c
  78. 2
      src/cpp/client/channel.cc
  79. 8
      src/cpp/client/client_context.cc
  80. 7
      src/cpp/client/secure_credentials.cc
  81. 42
      src/cpp/common/create_auth_context.h
  82. 45
      src/cpp/common/insecure_create_auth_context.cc
  83. 80
      src/cpp/common/secure_auth_context.cc
  84. 62
      src/cpp/common/secure_auth_context.h
  85. 50
      src/cpp/common/secure_create_auth_context.cc
  86. 99
      src/cpp/server/async_server_context.cc
  87. 13
      src/cpp/server/server.cc
  88. 18
      src/cpp/server/server_builder.cc
  89. 14
      src/cpp/server/server_context.cc
  90. 4
      src/csharp/Grpc.Auth/Grpc.Auth.csproj
  91. 6
      src/csharp/Grpc.Auth/Grpc.Auth.nuspec
  92. 1
      src/csharp/Grpc.Auth/Properties/AssemblyInfo.cs
  93. 6
      src/csharp/Grpc.Core.Tests/ClientServerTest.cs
  94. 5
      src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj
  95. 17
      src/csharp/Grpc.Core.Tests/GrpcEnvironmentTest.cs
  96. 12
      src/csharp/Grpc.Core.Tests/PInvokeTest.cs
  97. 1
      src/csharp/Grpc.Core.Tests/Properties/AssemblyInfo.cs
  98. 3
      src/csharp/Grpc.Core.Tests/ServerTest.cs
  99. 16
      src/csharp/Grpc.Core/Calls.cs
  100. 30
      src/csharp/Grpc.Core/Channel.cs
  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
objs
# Python virtual environment (pre-3.4 only)
python2.7_virtual_environment
# Python virtual environments
python*_virtual_environment
# gcov coverage data
coverage
@ -31,3 +31,5 @@ coverage
# vim temp files
.*.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-libtiff-compat main" | sudo tee -a /etc/apt/sources.list.d/mono-xamarin.list
- sudo apt-get update -qq
- sudo apt-get install -qq libgtest-dev libgflags-dev python-virtualenv 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 apt-get install -qq mono-devel nunit
- wget www.nuget.org/NuGet.exe -O nuget.exe

19
BUILD

@ -47,6 +47,7 @@ cc_library(
"src/core/support/env.h",
"src/core/support/file.h",
"src/core/support/murmur_hash.h",
"src/core/support/stack_lockfree.h",
"src/core/support/string.h",
"src/core/support/string_win32.h",
"src/core/support/thd_internal.h",
@ -73,6 +74,7 @@ cc_library(
"src/core/support/murmur_hash.c",
"src/core/support/slice.c",
"src/core/support/slice_buffer.c",
"src/core/support/stack_lockfree.c",
"src/core/support/string.c",
"src/core/support/string_posix.c",
"src/core/support/string_win32.c",
@ -138,6 +140,7 @@ cc_library(
"src/core/security/base64.h",
"src/core/security/credentials.h",
"src/core/security/json_token.h",
"src/core/security/jwt_verifier.h",
"src/core/security/secure_endpoint.h",
"src/core/security/secure_transport_setup.h",
"src/core/security/security_connector.h",
@ -254,6 +257,7 @@ cc_library(
"src/core/security/credentials_win32.c",
"src/core/security/google_default_credentials.c",
"src/core/security/json_token.c",
"src/core/security/jwt_verifier.c",
"src/core/security/secure_endpoint.c",
"src/core/security/secure_transport_setup.c",
"src/core/security/security_connector.c",
@ -346,6 +350,7 @@ cc_library(
"src/core/surface/server_chttp2.c",
"src/core/surface/server_create.c",
"src/core/surface/surface_trace.c",
"src/core/surface/version.c",
"src/core/transport/chttp2/alpn.c",
"src/core/transport/chttp2/bin_encoder.c",
"src/core/transport/chttp2/frame_data.c",
@ -576,6 +581,7 @@ cc_library(
"src/core/surface/server_chttp2.c",
"src/core/surface/server_create.c",
"src/core/surface/surface_trace.c",
"src/core/surface/version.c",
"src/core/transport/chttp2/alpn.c",
"src/core/transport/chttp2/bin_encoder.c",
"src/core/transport/chttp2/frame_data.c",
@ -627,11 +633,15 @@ cc_library(
name = "grpc++",
srcs = [
"src/cpp/client/secure_credentials.h",
"src/cpp/common/secure_auth_context.h",
"src/cpp/server/secure_server_credentials.h",
"src/cpp/client/channel.h",
"src/cpp/common/create_auth_context.h",
"src/cpp/server/thread_pool.h",
"src/cpp/client/secure_channel_arguments.cc",
"src/cpp/client/secure_credentials.cc",
"src/cpp/common/secure_auth_context.cc",
"src/cpp/common/secure_create_auth_context.cc",
"src/cpp/server/secure_server_credentials.cc",
"src/cpp/client/channel.cc",
"src/cpp/client/channel_arguments.cc",
@ -661,6 +671,7 @@ cc_library(
hdrs = [
"include/grpc++/async_generic_service.h",
"include/grpc++/async_unary_call.h",
"include/grpc++/auth_context.h",
"include/grpc++/byte_buffer.h",
"include/grpc++/channel_arguments.h",
"include/grpc++/channel_interface.h",
@ -713,7 +724,9 @@ cc_library(
name = "grpc++_unsecure",
srcs = [
"src/cpp/client/channel.h",
"src/cpp/common/create_auth_context.h",
"src/cpp/server/thread_pool.h",
"src/cpp/common/insecure_create_auth_context.cc",
"src/cpp/client/channel.cc",
"src/cpp/client/channel_arguments.cc",
"src/cpp/client/client_context.cc",
@ -742,6 +755,7 @@ cc_library(
hdrs = [
"include/grpc++/async_generic_service.h",
"include/grpc++/async_unary_call.h",
"include/grpc++/auth_context.h",
"include/grpc++/byte_buffer.h",
"include/grpc++/channel_arguments.h",
"include/grpc++/channel_interface.h",
@ -871,6 +885,7 @@ objc_library(
"src/core/support/murmur_hash.c",
"src/core/support/slice.c",
"src/core/support/slice_buffer.c",
"src/core/support/stack_lockfree.c",
"src/core/support/string.c",
"src/core/support/string_posix.c",
"src/core/support/string_win32.c",
@ -918,6 +933,7 @@ objc_library(
"src/core/support/env.h",
"src/core/support/file.h",
"src/core/support/murmur_hash.h",
"src/core/support/stack_lockfree.h",
"src/core/support/string.h",
"src/core/support/string_win32.h",
"src/core/support/thd_internal.h",
@ -946,6 +962,7 @@ objc_library(
"src/core/security/credentials_win32.c",
"src/core/security/google_default_credentials.c",
"src/core/security/json_token.c",
"src/core/security/jwt_verifier.c",
"src/core/security/secure_endpoint.c",
"src/core/security/secure_transport_setup.c",
"src/core/security/security_connector.c",
@ -1038,6 +1055,7 @@ objc_library(
"src/core/surface/server_chttp2.c",
"src/core/surface/server_create.c",
"src/core/surface/surface_trace.c",
"src/core/surface/version.c",
"src/core/transport/chttp2/alpn.c",
"src/core/transport/chttp2/bin_encoder.c",
"src/core/transport/chttp2/frame_data.c",
@ -1083,6 +1101,7 @@ objc_library(
"src/core/security/base64.h",
"src/core/security/credentials.h",
"src/core/security/json_token.h",
"src/core/security/jwt_verifier.h",
"src/core/security/secure_endpoint.h",
"src/core/security/secure_transport_setup.h",
"src/core/security/security_connector.h",

File diff suppressed because one or more lines are too long

@ -30,6 +30,7 @@
"public_headers": [
"include/grpc++/async_generic_service.h",
"include/grpc++/async_unary_call.h",
"include/grpc++/auth_context.h",
"include/grpc++/byte_buffer.h",
"include/grpc++/channel_arguments.h",
"include/grpc++/channel_interface.h",
@ -68,6 +69,7 @@
],
"headers": [
"src/cpp/client/channel.h",
"src/cpp/common/create_auth_context.h",
"src/cpp/server/thread_pool.h"
],
"src": [
@ -285,6 +287,7 @@
"src/core/surface/server_chttp2.c",
"src/core/surface/server_create.c",
"src/core/surface/surface_trace.c",
"src/core/surface/version.c",
"src/core/transport/chttp2/alpn.c",
"src/core/transport/chttp2/bin_encoder.c",
"src/core/transport/chttp2/frame_data.c",
@ -373,6 +376,7 @@
"src/core/support/env.h",
"src/core/support/file.h",
"src/core/support/murmur_hash.h",
"src/core/support/stack_lockfree.h",
"src/core/support/string.h",
"src/core/support/string_win32.h",
"src/core/support/thd_internal.h"
@ -401,6 +405,7 @@
"src/core/support/murmur_hash.c",
"src/core/support/slice.c",
"src/core/support/slice_buffer.c",
"src/core/support/stack_lockfree.c",
"src/core/support/string.c",
"src/core/support/string_posix.c",
"src/core/support/string_win32.c",
@ -451,6 +456,7 @@
"src/core/security/base64.h",
"src/core/security/credentials.h",
"src/core/security/json_token.h",
"src/core/security/jwt_verifier.h",
"src/core/security/secure_endpoint.h",
"src/core/security/secure_transport_setup.h",
"src/core/security/security_connector.h",
@ -473,6 +479,7 @@
"src/core/security/credentials_win32.c",
"src/core/security/google_default_credentials.c",
"src/core/security/json_token.c",
"src/core/security/jwt_verifier.c",
"src/core/security/secure_endpoint.c",
"src/core/security/secure_transport_setup.c",
"src/core/security/security_connector.c",
@ -557,11 +564,14 @@
"language": "c++",
"headers": [
"src/cpp/client/secure_credentials.h",
"src/cpp/common/secure_auth_context.h",
"src/cpp/server/secure_server_credentials.h"
],
"src": [
"src/cpp/client/secure_channel_arguments.cc",
"src/cpp/client/secure_credentials.cc",
"src/cpp/common/secure_auth_context.cc",
"src/cpp/common/secure_create_auth_context.cc",
"src/cpp/server/secure_server_credentials.cc"
],
"deps": [
@ -614,6 +624,9 @@
"name": "grpc++_unsecure",
"build": "all",
"language": "c++",
"src": [
"src/cpp/common/insecure_create_auth_context.cc"
],
"deps": [
"gpr",
"grpc_unsecure"
@ -1158,6 +1171,18 @@
"gpr"
]
},
{
"name": "gpr_stack_lockfree_test",
"build": "test",
"language": "c",
"src": [
"test/core/support/stack_lockfree_test.c"
],
"deps": [
"gpr_test_util",
"gpr"
]
},
{
"name": "gpr_string_test",
"build": "test",
@ -1356,6 +1381,20 @@
"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",
"build": "tool",
@ -1398,6 +1437,20 @@
"gpr"
]
},
{
"name": "grpc_verify_jwt",
"build": "tool",
"language": "c",
"src": [
"test/core/security/verify_jwt.c"
],
"deps": [
"grpc_test_util",
"grpc",
"gpr_test_util",
"gpr"
]
},
{
"name": "hpack_parser_test",
"build": "test",
@ -2256,11 +2309,11 @@
]
},
{
"name": "qps_test",
"name": "qps_openloop_test",
"build": "test",
"language": "c++",
"src": [
"test/cpp/qps/qps_test.cc"
"test/cpp/qps/qps_openloop_test.cc"
],
"deps": [
"qps",
@ -2274,11 +2327,11 @@
]
},
{
"name": "qps_test_openloop",
"name": "qps_test",
"build": "test",
"language": "c++",
"src": [
"test/cpp/qps/qps_test_openloop.cc"
"test/cpp/qps/qps_test.cc"
],
"deps": [
"qps",
@ -2313,6 +2366,19 @@
"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",
"build": "test",

@ -8,58 +8,39 @@ requests) and instead do some form of exponential backoff.
We have several parameters:
1. INITIAL_BACKOFF (how long to wait after the first failure before retrying)
2. MULTIPLIER (factor with which to multiply backoff after a failed retry)
3. MAX_BACKOFF (Upper bound on backoff)
4. MIN_CONNECTION_TIMEOUT
3. MAX_BACKOFF (upper bound on backoff)
4. MIN_CONNECT_TIMEOUT (minimum time we're willing to give a connection to
complete)
## Proposed Backoff Algorithm
Exponentially back off the start time of connection attempts up to a limit of
MAX_BACKOFF.
MAX_BACKOFF, with jitter.
```
ConnectWithBackoff()
current_backoff = 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)
SleepUntil(current_deadline)
current_backoff = Min(current_backoff * MULTIPLIER, MAX_BACKOFF)
current_deadline = now() + 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.
current_deadline = now() + current_backoff +
UniformRandom(-JITTER * current_backoff, JITTER * current_backoff)
```
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
of 1 second, multiplier of 1.2, and a maximum backoff of 120 seconds.
With specific parameters of
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
maintenance
* Client tries to connect to a server which is overloaded
* 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.
Alternate implementations must ensure that connection backoffs started at the
same time disperse, and must not attempt connections substantially more often
than the above algorithm.

@ -392,6 +392,50 @@ Asserts:
* clients are free to assert that the response payload body contents are zero
and comparing the entire response message against a golden response
### oauth2_auth_token
Similar to the other auth tests, this test is only for cloud-to-prod path.
This test verifies unary calls succeed in sending messages using an OAuth2 token that is obtained OOB. 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 use the auth library to obtain an authorization token
2. Client calls UnaryCall, attaching the authorization token obtained in step1, with the following message
```
{
response_type: COMPRESSABLE
response_size: 314159
payload:{
body: 271828 bytes of zeros
}
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`
* response payload body is 314159 bytes in size
* clients are free to assert that the response payload body contents are zero
and comparing the entire response message against a golden response
### Metadata (TODO: fix name)
Status: Not yet implementable
@ -560,11 +604,6 @@ Propagation of status code and message (yangg)
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
#### Normal priority:

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

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

@ -40,6 +40,7 @@
#include <grpc/support/log.h>
#include <grpc/support/time.h>
#include <grpc++/auth_context.h>
#include <grpc++/config.h>
#include <grpc++/status.h>
#include <grpc++/time.h>
@ -108,6 +109,8 @@ class ClientContext {
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_; }
@ -159,6 +162,7 @@ class ClientContext {
gpr_timespec deadline_;
grpc::string authority_;
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> recv_initial_metadata_;

@ -120,6 +120,12 @@ std::shared_ptr<Credentials> JWTCredentials(const grpc::string& json_key,
std::shared_ptr<Credentials> RefreshTokenCredentials(
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.
std::shared_ptr<Credentials> IAMCredentials(
const grpc::string& authorization_token,

@ -60,6 +60,93 @@ void FillMetadataMap(grpc_metadata_array* arr,
grpc_metadata* FillMetadataArray(
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
/// used for generating multiple names for the same thing.
template <int I>
@ -104,6 +191,12 @@ class CallOpSendMessage {
public:
CallOpSendMessage() : send_buf_(nullptr), own_buf_(false) {}
/// Send \a message using \a options for the write. The \a options are cleared
/// after use.
template <class M>
Status SendMessage(const M& message,
const WriteOptions& options) GRPC_MUST_USE_RESULT;
template <class M>
Status SendMessage(const M& message) GRPC_MUST_USE_RESULT;
@ -112,8 +205,10 @@ class CallOpSendMessage {
if (send_buf_ == nullptr) return;
grpc_op* op = &ops[(*nops)++];
op->op = GRPC_OP_SEND_MESSAGE;
op->flags = 0;
op->flags = write_options_.flags();
op->data.send_message = send_buf_;
// Flags are per-message: clear them after use.
write_options_.Clear();
}
void FinishOp(bool* status, int max_message_size) {
if (own_buf_) grpc_byte_buffer_destroy(send_buf_);
@ -122,14 +217,22 @@ class CallOpSendMessage {
private:
grpc_byte_buffer* send_buf_;
WriteOptions write_options_;
bool own_buf_;
};
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_);
}
template <class M>
Status CallOpSendMessage::SendMessage(const M& message) {
return SendMessage(message, WriteOptions());
}
template <class R>
class CallOpRecvMessage {
public:
@ -172,17 +275,34 @@ class CallOpRecvMessage {
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 {
public:
CallOpGenericRecvMessage() : got_message(false) {}
template <class R>
void RecvMessage(R* message) {
deserialize_ = [message](grpc_byte_buffer* buf,
int max_message_size) -> Status {
return SerializationTraits<R>::Deserialize(buf, message,
max_message_size);
};
deserialize_.reset(
new CallOpGenericRecvMessageHelper::DeserializeFuncType<R>(message));
}
bool got_message;
@ -201,7 +321,7 @@ class CallOpGenericRecvMessage {
if (recv_buf_) {
if (*status) {
got_message = true;
*status = deserialize_(recv_buf_, max_message_size).ok();
*status = deserialize_->Deserialize(recv_buf_, max_message_size).ok();
} else {
got_message = false;
grpc_byte_buffer_destroy(recv_buf_);
@ -210,12 +330,11 @@ class CallOpGenericRecvMessage {
got_message = false;
*status = false;
}
deserialize_ = DeserializeFunc();
deserialize_.reset();
}
private:
typedef std::function<Status(grpc_byte_buffer*, int)> DeserializeFunc;
DeserializeFunc deserialize_;
std::unique_ptr<CallOpGenericRecvMessageHelper::DeserializeFunc> deserialize_;
grpc_byte_buffer* recv_buf_;
};

@ -84,8 +84,8 @@ class Server GRPC_FINAL : public GrpcLibrary, private CallHook {
int max_message_size);
// Register a service. This call does not take ownership of the service.
// The service must exist for the lifetime of the Server instance.
bool RegisterService(RpcService* service);
bool RegisterAsyncService(AsynchronousService* service);
bool RegisterService(const grpc::string *host, RpcService* service);
bool RegisterAsyncService(const grpc::string *host, AsynchronousService* service);
void RegisterAsyncGenericService(AsyncGenericService* service);
// Add a listening port. Can be called multiple times.
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.
// The service must exist for the lifetime of the Server instance returned by
// BuildAndStart().
// Matches requests with any :authority
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.
// The service and completion queuemust exist for the lifetime of the Server
// instance returned by BuildAndStart().
// Matches requests with any :authority
void RegisterAsyncService(AsynchronousService* service);
// Register a generic service.
// Matches requests with any :authority
void RegisterAsyncGenericService(AsyncGenericService* service);
// Register a service. This call does not take ownership of the service.
// The service must exist for the lifetime of the 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.
void SetMaxMessageSize(int max_message_size) {
max_message_size_ = max_message_size;
@ -98,9 +116,18 @@ class ServerBuilder {
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_;
std::vector<RpcService*> services_;
std::vector<AsynchronousService*> async_services_;
std::vector<std::unique_ptr<NamedService<RpcService>>> services_;
std::vector<std::unique_ptr<NamedService<AsynchronousService>>> async_services_;
std::vector<Port> ports_;
std::vector<ServerCompletionQueue*> cqs_;
std::shared_ptr<ServerCredentials> creds_;

@ -35,8 +35,10 @@
#define GRPCXX_SERVER_CONTEXT_H
#include <map>
#include <memory>
#include <grpc/support/time.h>
#include <grpc++/auth_context.h>
#include <grpc++/config.h>
#include <grpc++/time.h>
@ -97,6 +99,8 @@ class ServerContext {
return client_metadata_;
}
std::shared_ptr<const AuthContext> auth_context() const;
private:
friend class ::grpc::Server;
template <class W, class R>
@ -133,12 +137,15 @@ class ServerContext {
ServerContext(gpr_timespec deadline, grpc_metadata* metadata,
size_t metadata_count);
void set_call(grpc_call* call);
CompletionOp* completion_op_;
gpr_timespec deadline_;
grpc_call* call_;
CompletionQueue* cq_;
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> initial_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.
// 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>
@ -168,9 +172,10 @@ class ClientWriter : public ClientWriterInterface<W> {
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;
if (!ops.SendMessage(msg).ok()) {
if (!ops.SendMessage(msg, options).ok()) {
return false;
}
call_.PerformOps(&ops);
@ -246,9 +251,10 @@ class ClientReaderWriter GRPC_FINAL : public ClientReaderWriterInterface<W, R> {
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;
if (!ops.SendMessage(msg).ok()) return false;
if (!ops.SendMessage(msg, options).ok()) return false;
call_.PerformOps(&ops);
return cq_.Pluck(&ops);
}
@ -317,9 +323,10 @@ class ServerWriter GRPC_FINAL : public WriterInterface<W> {
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;
if (!ops.SendMessage(msg).ok()) {
if (!ops.SendMessage(msg, options).ok()) {
return false;
}
if (!ctx_->sent_initial_metadata_) {
@ -359,9 +366,10 @@ class ServerReaderWriter GRPC_FINAL : public WriterInterface<W>,
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;
if (!ops.SendMessage(msg).ok()) {
if (!ops.SendMessage(msg, options).ok()) {
return false;
}
if (!ctx_->sent_initial_metadata_) {

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

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

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

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

@ -57,13 +57,12 @@ void PrintProtoRpcDeclarationAsPragma(Printer *printer,
vars["server_stream"] = method->server_streaming() ? "stream " : "";
printer->Print(vars,
"#pragma mark $method_name$($client_stream$$request_type$)"
" returns ($server_stream$$response_type$)\n\n");
"#pragma mark $method_name$($client_stream$$request_type$)"
" returns ($server_stream$$response_type$)\n\n");
}
void PrintMethodSignature(Printer *printer,
const MethodDescriptor *method,
const map<string, string>& vars) {
void PrintMethodSignature(Printer *printer, const MethodDescriptor *method,
const map<string, string> &vars) {
// TODO(jcanizales): Print method comments.
printer->Print(vars, "- ($return_type$)$method_name$With");
@ -75,16 +74,17 @@ void PrintMethodSignature(Printer *printer,
// TODO(jcanizales): Put this on a new line and align colons.
if (method->server_streaming()) {
printer->Print(vars, " eventHandler:(void(^)(BOOL done, "
"$response_class$ *response, NSError *error))eventHandler");
printer->Print(vars,
" eventHandler:(void(^)(BOOL done, "
"$response_class$ *response, NSError *error))eventHandler");
} else {
printer->Print(vars, " handler:(void(^)($response_class$ *response, "
"NSError *error))handler");
printer->Print(vars,
" handler:(void(^)($response_class$ *response, "
"NSError *error))handler");
}
}
void PrintSimpleSignature(Printer *printer,
const MethodDescriptor *method,
void PrintSimpleSignature(Printer *printer, const MethodDescriptor *method,
map<string, string> vars) {
vars["method_name"] =
grpc_generator::LowercaseFirstLetter(vars["method_name"]);
@ -92,8 +92,7 @@ void PrintSimpleSignature(Printer *printer,
PrintMethodSignature(printer, method, vars);
}
void PrintAdvancedSignature(Printer *printer,
const MethodDescriptor *method,
void PrintAdvancedSignature(Printer *printer, const MethodDescriptor *method,
map<string, string> vars) {
vars["method_name"] = "RPCTo" + vars["method_name"];
vars["return_type"] = "ProtoRPC *";
@ -101,15 +100,16 @@ void PrintAdvancedSignature(Printer *printer,
}
inline map<string, string> GetMethodVars(const MethodDescriptor *method) {
return {{ "method_name", method->name() },
{ "request_type", method->input_type()->name() },
{ "response_type", method->output_type()->name() },
{ "request_class", ClassName(method->input_type()) },
{ "response_class", ClassName(method->output_type()) }};
map<string, string> res;
res["method_name"] = method->name();
res["request_type"] = method->input_type()->name();
res["response_type"] = method->output_type()->name();
res["request_class"] = ClassName(method->input_type());
res["response_class"] = ClassName(method->output_type());
return res;
}
void PrintMethodDeclarations(Printer *printer,
const MethodDescriptor *method) {
void PrintMethodDeclarations(Printer *printer, const MethodDescriptor *method) {
map<string, string> vars = GetMethodVars(method);
PrintProtoRpcDeclarationAsPragma(printer, method, vars);
@ -120,8 +120,7 @@ void PrintMethodDeclarations(Printer *printer,
printer->Print(";\n\n\n");
}
void PrintSimpleImplementation(Printer *printer,
const MethodDescriptor *method,
void PrintSimpleImplementation(Printer *printer, const MethodDescriptor *method,
map<string, string> vars) {
printer->Print("{\n");
printer->Print(vars, " [[self RPCTo$method_name$With");
@ -178,7 +177,7 @@ void PrintMethodImplementations(Printer *printer,
PrintAdvancedImplementation(printer, method, vars);
}
} // namespace
} // namespace
string GetHeader(const ServiceDescriptor *service) {
string output;
@ -186,7 +185,7 @@ string GetHeader(const ServiceDescriptor *service) {
// Scope the output stream so it closes and finalizes output to the string.
grpc::protobuf::io::StringOutputStream output_stream(&output);
Printer printer(&output_stream, '$');
printer.Print("@protocol GRXWriteable;\n");
printer.Print("@protocol GRXWriter;\n\n");
@ -199,12 +198,15 @@ string GetHeader(const ServiceDescriptor *service) {
}
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");
printer.Print(vars, "@interface $service_class$ :"
" ProtoService<$service_class$>\n");
printer.Print("- (instancetype)initWithHost:(NSString *)host"
" NS_DESIGNATED_INITIALIZER;\n");
printer.Print(vars,
"@interface $service_class$ :"
" ProtoService<$service_class$>\n");
printer.Print(
"- (instancetype)initWithHost:(NSString *)host"
" NS_DESIGNATED_INITIALIZER;\n");
printer.Print("@end\n");
}
return output;
@ -222,18 +224,20 @@ string GetSource(const ServiceDescriptor *service) {
{"package", service->file()->package()}};
printer.Print(vars,
"static NSString *const kPackageName = @\"$package$\";\n");
printer.Print(vars,
"static NSString *const kServiceName = @\"$service_name$\";\n\n");
"static NSString *const kPackageName = @\"$package$\";\n");
printer.Print(
vars, "static NSString *const kServiceName = @\"$service_name$\";\n\n");
printer.Print(vars, "@implementation $service_class$\n\n");
printer.Print("// Designated initializer\n");
printer.Print("- (instancetype)initWithHost:(NSString *)host {\n");
printer.Print(" return (self = [super initWithHost:host"
printer.Print(
" return (self = [super initWithHost:host"
" packageName:kPackageName serviceName:kServiceName]);\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");
printer.Print("- (instancetype)initWithHost:(NSString *)host\n");
printer.Print(" packageName:(NSString *)packageName\n");
@ -250,4 +254,4 @@ string GetSource(const ServiceDescriptor *service) {
return output;
}
} // namespace grpc_objective_c_generator
} // namespace grpc_objective_c_generator

@ -151,7 +151,7 @@ static void client_init_call_elem(grpc_call_element* elem,
call_data* d = elem->call_data;
GPR_ASSERT(d != NULL);
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();
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;
GPR_ASSERT(d != NULL);
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();
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) {
call_data* d = elem->call_data;
GPR_ASSERT(d != NULL);
d->stats.elapsed_time_ms =
gpr_timespec_to_micros(gpr_time_sub(gpr_now(), d->start_ts));
d->stats.elapsed_time_ms = gpr_timespec_to_micros(
gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME), d->start_ts));
census_record_rpc_server_stats(d->op_id, &d->stats);
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;
GPR_ASSERT(chand != NULL);
if (chand->path_str != NULL) {
grpc_mdstr_unref(chand->path_str);
GRPC_MDSTR_UNREF(chand->path_str);
}
}

@ -108,13 +108,13 @@ static void hc_mutate_op(grpc_call_element *elem,
/* Send : prefixed headers, which have to be before any application
layer headers. */
grpc_metadata_batch_add_head(&op->data.metadata, &calld->method,
grpc_mdelem_ref(channeld->method));
GRPC_MDELEM_REF(channeld->method));
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_mdelem_ref(channeld->te_trailers));
GRPC_MDELEM_REF(channeld->te_trailers));
grpc_metadata_batch_add_tail(&op->data.metadata, &calld->content_type,
grpc_mdelem_ref(channeld->content_type));
GRPC_MDELEM_REF(channeld->content_type));
break;
}
}
@ -196,11 +196,11 @@ static void destroy_channel_elem(grpc_channel_element *elem) {
/* grab pointers to our data from the channel element */
channel_data *channeld = elem->channel_data;
grpc_mdelem_unref(channeld->te_trailers);
grpc_mdelem_unref(channeld->method);
grpc_mdelem_unref(channeld->scheme);
grpc_mdelem_unref(channeld->content_type);
grpc_mdelem_unref(channeld->status);
GRPC_MDELEM_UNREF(channeld->te_trailers);
GRPC_MDELEM_UNREF(channeld->method);
GRPC_MDELEM_UNREF(channeld->scheme);
GRPC_MDELEM_UNREF(channeld->content_type);
GRPC_MDELEM_UNREF(channeld->status);
}
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
omitted */
grpc_mdelem *authority = grpc_mdelem_from_metadata_strings(
channeld->mdctx, grpc_mdstr_ref(channeld->authority_key),
grpc_mdstr_ref(md->value));
grpc_mdelem_unref(md);
channeld->mdctx, GRPC_MDSTR_REF(channeld->authority_key),
GRPC_MDSTR_REF(md->value));
GRPC_MDELEM_UNREF(md);
return authority;
} else {
return md;
@ -193,7 +193,7 @@ static void hs_mutate_op(grpc_call_element *elem,
if (op->type != GRPC_OP_METADATA) continue;
calld->sent_status = 1;
grpc_metadata_batch_add_head(&op->data.metadata, &calld->status,
grpc_mdelem_ref(channeld->status_ok));
GRPC_MDELEM_REF(channeld->status_ok));
break;
}
}
@ -264,17 +264,17 @@ static void destroy_channel_elem(grpc_channel_element *elem) {
/* grab pointers to our data from the channel element */
channel_data *channeld = elem->channel_data;
grpc_mdelem_unref(channeld->te_trailers);
grpc_mdelem_unref(channeld->status_ok);
grpc_mdelem_unref(channeld->status_not_found);
grpc_mdelem_unref(channeld->method_post);
grpc_mdelem_unref(channeld->http_scheme);
grpc_mdelem_unref(channeld->https_scheme);
grpc_mdelem_unref(channeld->grpc_scheme);
grpc_mdelem_unref(channeld->content_type);
grpc_mdstr_unref(channeld->path_key);
grpc_mdstr_unref(channeld->authority_key);
grpc_mdstr_unref(channeld->host_key);
GRPC_MDELEM_UNREF(channeld->te_trailers);
GRPC_MDELEM_UNREF(channeld->status_ok);
GRPC_MDELEM_UNREF(channeld->status_not_found);
GRPC_MDELEM_UNREF(channeld->method_post);
GRPC_MDELEM_UNREF(channeld->http_scheme);
GRPC_MDELEM_UNREF(channeld->https_scheme);
GRPC_MDELEM_UNREF(channeld->grpc_scheme);
GRPC_MDELEM_UNREF(channeld->content_type);
GRPC_MDSTR_UNREF(channeld->path_key);
GRPC_MDSTR_UNREF(channeld->authority_key);
GRPC_MDSTR_UNREF(channeld->host_key);
}
const grpc_channel_filter grpc_http_server_filter = {

@ -300,7 +300,7 @@ static void continue_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->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->next_attempt = gpr_time_add(c->next_attempt, 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);
}
}

@ -85,7 +85,7 @@ typedef struct grpc_httpcli_response {
char *body;
} 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,
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
destroyed once the call returns
'deadline' contains a deadline for the request (or gpr_inf_future)
'em' points to a caller owned event manager that must be alive for the
lifetime of the request
'on_response' is a callback to report results to (and 'user_data' is a user
supplied pointer to pass to said call) */
void grpc_httpcli_get(grpc_httpcli_context *context, grpc_pollset *pollset,

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

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

@ -105,10 +105,11 @@ static void multipoll_with_epoll_pollset_maybe_work(
* here.
*/
timeout_ms = grpc_poll_deadline_to_millis_timeout(deadline, now);
pollset->counter += 1;
gpr_mu_unlock(&pollset->mu);
timeout_ms = grpc_poll_deadline_to_millis_timeout(deadline, now);
do {
ep_rv = epoll_wait(h->epoll_fd, ep_ev, GRPC_EPOLL_MAX_EVENTS, timeout_ms);
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) {
/* pollset->mu already held */
gpr_timespec now = gpr_now();
gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME);
if (gpr_time_cmp(now, deadline) > 0) {
return 0;
}
@ -187,15 +187,16 @@ void grpc_pollset_destroy(grpc_pollset *pollset) {
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;
static const int max_spin_polling_us = 10;
if (gpr_time_cmp(deadline, gpr_inf_future) == 0) {
return -1;
}
if (gpr_time_cmp(
deadline,
gpr_time_add(now, gpr_time_from_micros(max_spin_polling_us))) <= 0) {
deadline,
gpr_time_add(now, gpr_time_from_micros(max_spin_polling_us))) <= 0) {
return 0;
}
timeout = gpr_time_sub(deadline, now);

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

@ -37,6 +37,7 @@
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
#include "src/core/iomgr/iocp_windows.h"
#include "src/core/iomgr/iomgr_internal.h"
@ -45,11 +46,14 @@
#include "src/core/iomgr/socket_windows.h"
grpc_winsocket *grpc_winsocket_create(SOCKET socket, const char *name) {
char *final_name;
grpc_winsocket *r = gpr_malloc(sizeof(grpc_winsocket));
memset(r, 0, sizeof(grpc_winsocket));
r->socket = socket;
gpr_mu_init(&r->state_mu);
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);
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
various callsites of that function, which happens to be in various
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;
gpr_mu_lock(&socket->state_mu);
if (socket->read_info.cb) {
SOCKET socket;
gpr_mu_lock(&winsocket->state_mu);
socket = winsocket->socket;
if (winsocket->read_info.cb) {
callbacks_set++;
grpc_iomgr_closure_init(&socket->shutdown_closure, socket->read_info.cb,
socket->read_info.opaque);
grpc_iomgr_add_delayed_callback(&socket->shutdown_closure, 0);
grpc_iomgr_closure_init(&winsocket->shutdown_closure,
winsocket->read_info.cb,
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++;
grpc_iomgr_closure_init(&socket->shutdown_closure, socket->write_info.cb,
socket->write_info.opaque);
grpc_iomgr_add_delayed_callback(&socket->shutdown_closure, 0);
grpc_iomgr_closure_init(&winsocket->shutdown_closure,
winsocket->write_info.cb,
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;
}
@ -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
both memory and sockets. */
void grpc_winsocket_orphan(grpc_winsocket *winsocket) {
SOCKET socket = winsocket->socket;
grpc_iomgr_unregister_object(&winsocket->iomgr_object);
if (winsocket->read_info.outstanding || winsocket->write_info.outstanding) {
grpc_iocp_socket_orphan(winsocket);
} else {
grpc_winsocket_destroy(winsocket);
}
closesocket(socket);
}
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_arg = ac->cb_arg;
gpr_mu_lock(&ac->mu);
if (success) {
do {
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:
don't do that! */
gpr_log(GPR_ERROR, "kernel out of buffers");
gpr_mu_unlock(&ac->mu);
grpc_fd_notify_on_write(ac->fd, &ac->write_closure);
return;
} else {
@ -165,10 +167,11 @@ static void on_writable(void *acp, int success) {
abort();
finish:
gpr_mu_lock(&ac->mu);
if (!ep) {
if (ep == NULL) {
grpc_pollset_set_del_fd(ac->interested_parties, ac->fd);
grpc_fd_orphan(ac->fd, NULL, "tcp_client_orphan");
} else {
ac->fd = NULL;
}
done = (--ac->refs == 0);
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;
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);
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->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;
grpc_socket_notify_on_write(socket, on_connect, ac);
return;

@ -108,9 +108,10 @@ void grpc_tcp_server_destroy(grpc_tcp_server *s,
size_t i;
gpr_mu_lock(&s->mu);
/* 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++) {
server_port *sp = &s->ports[i];
sp->shutting_down = 1;
grpc_winsocket_shutdown(sp->socket);
}
/* 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;
grpc_winsocket_callback_info *info = &sp->socket->read_info;
grpc_endpoint *ep = NULL;
/* The shutdown sequence is done in two parts. This is the second
part here, acknowledging the IOCP notification, and doing nothing
else, especially not queuing a new accept. */
if (sp->shutting_down) {
GPR_ASSERT(from_iocp);
sp->shutting_down = 0;
sp->socket->read_info.outstanding = 0;
gpr_mu_lock(&sp->server->mu);
if (0 == --sp->server->active_ports) {
gpr_cv_broadcast(&sp->server->cv);
}
gpr_mu_unlock(&sp->server->mu);
return;
}
if (from_iocp) {
/* The IOCP notified us of a completed operation. Let's grab the results,
and act accordingly. */
DWORD transfered_bytes = 0;
DWORD flags;
BOOL wsa_success = WSAGetOverlappedResult(sock, &info->overlapped,
&transfered_bytes, FALSE, &flags);
if (!wsa_success) {
DWORD transfered_bytes;
DWORD flags;
BOOL wsa_success;
/* The general mechanism for shutting down is to queue abortion calls. While
this is necessary in the read/write case, it's useless for the accept
case. Let's do nothing. */
if (!from_iocp) return;
/* The IOCP notified us of a completed operation. Let's grab the results,
and act accordingly. */
transfered_bytes = 0;
wsa_success = WSAGetOverlappedResult(sock, &info->overlapped,
&transfered_bytes, FALSE, &flags);
if (!wsa_success) {
if (sp->shutting_down) {
/* During the shutdown case, we ARE expecting an error. So that's swell,
and we can wake up the shutdown thread. */
sp->shutting_down = 0;
sp->socket->read_info.outstanding = 0;
gpr_mu_lock(&sp->server->mu);
if (0 == --sp->server->active_ports) {
gpr_cv_broadcast(&sp->server->cv);
}
gpr_mu_unlock(&sp->server->mu);
return;
} else {
char *utf8_message = gpr_format_message(WSAGetLastError());
gpr_log(GPR_ERROR, "on_accept error: %s", utf8_message);
gpr_free(utf8_message);
closesocket(sock);
} else {
/* TODO(ctiller): add sockaddr address to label */
ep = grpc_tcp_create(grpc_winsocket_create(sock, "server"));
}
} else {
/* If we're not notified from the IOCP, it means we are asked to shutdown.
This will initiate that shutdown. Calling closesocket will trigger an
IOCP notification, that will call this function a second time, from
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);
if (!sp->shutting_down) {
/* TODO(ctiller): add sockaddr address to label */
ep = grpc_tcp_create(grpc_winsocket_create(sock, "server"));
}
return;
}
/* The only time we should call our callback, is where we successfully
managed to accept a connection, and created an endpoint. */
if (ep) sp->server->cb(sp->server->cb_arg, ep);
/* As we were notified from the IOCP of one and exactly one accept,
the former socked we created has now either been destroy or assigned
to the new connection. We need to create a new one for the next
connection. */
the former socked we created has now either been destroy or assigned
to the new connection. We need to create a new one for the next
connection. */
start_accept(sp);
}

@ -148,9 +148,11 @@ static void on_read(void *tcpp, int from_iocp) {
GPR_ASSERT(tcp->socket->read_info.outstanding);
if (socket->read_info.wsa_error != 0) {
char *utf8_message = gpr_format_message(info->wsa_error);
gpr_log(GPR_ERROR, "ReadFile overlapped error: %s", utf8_message);
gpr_free(utf8_message);
if (socket->read_info.wsa_error != WSAECONNRESET) {
char *utf8_message = gpr_format_message(info->wsa_error);
gpr_log(GPR_ERROR, "ReadFile overlapped error: %s", utf8_message);
gpr_free(utf8_message);
}
status = GRPC_ENDPOINT_CB_ERROR;
} else {
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);
if (info->wsa_error != 0) {
char *utf8_message = gpr_format_message(info->wsa_error);
gpr_log(GPR_ERROR, "WSASend overlapped error: %s", utf8_message);
gpr_free(utf8_message);
if (info->wsa_error != WSAECONNRESET) {
char *utf8_message = gpr_format_message(info->wsa_error);
gpr_log(GPR_ERROR, "WSASend overlapped error: %s", utf8_message);
gpr_free(utf8_message);
}
status = GRPC_ENDPOINT_CB_ERROR;
} else {
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;
GPR_ASSERT(bytes_sent == tcp->write_slices.length);
} else {
char *utf8_message = gpr_format_message(info->wsa_error);
gpr_log(GPR_ERROR, "WSASend error: %s", utf8_message);
gpr_free(utf8_message);
if (socket->read_info.wsa_error != WSAECONNRESET) {
char *utf8_message = gpr_format_message(info->wsa_error);
gpr_log(GPR_ERROR, "WSASend error: %s", utf8_message);
gpr_free(utf8_message);
}
}
if (allocated) gpr_free(allocated);
gpr_slice_buffer_reset_and_unref(&tcp->write_slices);

@ -82,7 +82,7 @@ struct grpc_precise_clock {
gpr_timespec clock;
};
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_PRINTF_ARGS(clk) \

@ -61,6 +61,7 @@ typedef struct {
grpc_transport_stream_op op;
size_t op_md_idx;
int sent_initial_metadata;
gpr_uint8 security_context_set;
grpc_linked_mdelem md_links[MAX_CREDENTIALS_METADATA_COUNT];
} call_data;
@ -199,8 +200,22 @@ static void auth_start_transport_op(grpc_call_element *elem,
channel_data *chand = elem->channel_data;
grpc_linked_mdelem *l;
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) {
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.
*/
if (md->key == chand->authority_string) {
if (calld->host != NULL) grpc_mdstr_unref(calld->host);
calld->host = grpc_mdstr_ref(md->value);
if (calld->host != NULL) GRPC_MDSTR_UNREF(calld->host);
calld->host = GRPC_MDSTR_REF(md->value);
} else if (md->key == chand->path_string) {
if (calld->method != NULL) grpc_mdstr_unref(calld->method);
calld->method = grpc_mdstr_ref(md->value);
if (calld->method != NULL) GRPC_MDSTR_UNREF(calld->method);
calld->method = GRPC_MDSTR_REF(md->value);
}
}
if (calld->host != NULL) {
@ -263,6 +278,7 @@ static void init_call_elem(grpc_call_element *elem,
calld->method = NULL;
calld->pollset = NULL;
calld->sent_initial_metadata = 0;
calld->security_context_set = 0;
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;
grpc_credentials_unref(calld->creds);
if (calld->host != NULL) {
grpc_mdstr_unref(calld->host);
GRPC_MDSTR_UNREF(calld->host);
}
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)
GRPC_SECURITY_CONNECTOR_UNREF(&ctx->base, "client_auth_filter");
if (chand->authority_string != NULL) {
grpc_mdstr_unref(chand->authority_string);
GRPC_MDSTR_UNREF(chand->authority_string);
}
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) {
grpc_mdstr_unref(chand->status_key);
GRPC_MDSTR_UNREF(chand->status_key);
}
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/httpcli/httpcli.h"
#include "src/core/iomgr/iomgr.h"
#include "src/core/security/json_token.h"
#include "src/core/support/string.h"
#include <grpc/support/alloc.h>
@ -52,12 +51,12 @@
/* -- Common. -- */
typedef struct {
struct grpc_credentials_metadata_request {
grpc_credentials *creds;
grpc_credentials_metadata_cb cb;
grpc_iomgr_closure *on_simulated_token_fetch_done_closure;
void *user_data;
} grpc_credentials_metadata_request;
};
static grpc_credentials_metadata_request *
grpc_credentials_metadata_request_create(grpc_credentials *creds,
@ -152,16 +151,6 @@ grpc_security_status grpc_server_credentials_create_security_connector(
/* -- 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) {
grpc_ssl_credentials *c = (grpc_ssl_credentials *)creds;
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 -- */
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) {
if (c->cached.jwt_md != NULL) {
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 &&
strcmp(c->cached.service_url, service_url) == 0 &&
c->cached.jwt_md != NULL &&
(gpr_time_cmp(gpr_time_sub(c->cached.jwt_expiration, gpr_now()),
(gpr_time_cmp(gpr_time_sub(c->cached.jwt_expiration,
gpr_now(GPR_CLOCK_REALTIME)),
refresh_threshold) > 0)) {
jwt_md = grpc_credentials_md_store_ref(c->cached.jwt_md);
}
@ -401,7 +375,8 @@ static void jwt_get_request_metadata(grpc_credentials *creds,
char *md_value;
gpr_asprintf(&md_value, "Bearer %s", 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.jwt_md = grpc_credentials_md_store_create(1);
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_get_request_metadata, NULL};
grpc_credentials *grpc_jwt_credentials_create(const char *json_key,
gpr_timespec token_lifetime) {
grpc_credentials *grpc_jwt_credentials_create_from_auth_json_key(
grpc_auth_json_key key, gpr_timespec token_lifetime) {
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)) {
gpr_log(GPR_ERROR, "Invalid input for jwt credentials creation");
return NULL;
@ -444,26 +418,13 @@ grpc_credentials *grpc_jwt_credentials_create(const char *json_key,
return &c->base;
}
/* -- Oauth2TokenFetcher credentials -- */
/* This object is a base for credentials that need to acquire an oauth2 token
from an http service. */
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);
grpc_credentials *grpc_jwt_credentials_create(const char *json_key,
gpr_timespec token_lifetime) {
return grpc_jwt_credentials_create_from_auth_json_key(
grpc_auth_json_key_create_from_string(json_key), token_lifetime);
}
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_pollset_set pollset_set;
grpc_fetch_oauth2_func fetch_func;
} grpc_oauth2_token_fetcher_credentials;
/* -- Oauth2TokenFetcher credentials -- */
static void oauth2_token_fetcher_destroy(grpc_credentials *creds) {
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(
response, &c->access_token_md, &token_lifetime);
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,
c->access_token_md->num_entries, status);
} else {
@ -608,8 +570,9 @@ static void oauth2_token_fetcher_get_request_metadata(
{
gpr_mu_lock(&c->mu);
if (c->access_token_md != NULL &&
(gpr_time_cmp(gpr_time_sub(c->token_expiration, gpr_now()),
refresh_threshold) > 0)) {
(gpr_time_cmp(
gpr_time_sub(c->token_expiration, gpr_now(GPR_CLOCK_REALTIME)),
refresh_threshold) > 0)) {
cached_access_token_md =
grpc_credentials_md_store_ref(c->access_token_md);
}
@ -623,7 +586,7 @@ static void oauth2_token_fetcher_get_request_metadata(
c->fetch_func(
grpc_credentials_metadata_request_create(creds, cb, user_data),
&c->httpcli_context, pollset, on_oauth2_token_fetcher_http_response,
gpr_time_add(gpr_now(), 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);
c->token_expiration = gpr_inf_past;
c->fetch_func = fetch_func;
grpc_pollset_set_init(&c->pollset_set);
grpc_httpcli_context_init(&c->httpcli_context);
}
/* -- ComputeEngine credentials. -- */
@ -670,13 +633,6 @@ grpc_credentials *grpc_compute_engine_credentials_create(void) {
/* -- 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) {
grpc_service_account_credentials *c =
(grpc_service_account_credentials *)creds;
@ -747,11 +703,6 @@ grpc_credentials *grpc_service_account_credentials_create(
/* -- 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) {
grpc_refresh_token_credentials *c = (grpc_refresh_token_credentials *)creds;
grpc_auth_refresh_token_destruct(&c->refresh_token);
@ -787,12 +738,9 @@ static void refresh_token_fetch_oauth2(
gpr_free(body);
}
grpc_credentials *grpc_refresh_token_credentials_create(
const char *json_refresh_token) {
grpc_credentials *grpc_refresh_token_credentials_create_from_auth_refresh_token(
grpc_auth_refresh_token refresh_token) {
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)) {
gpr_log(GPR_ERROR, "Invalid input for refresh token credentials creation");
return NULL;
@ -805,13 +753,13 @@ grpc_credentials *grpc_refresh_token_credentials_create(
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 {
grpc_credentials base;
grpc_credentials_md_store *access_token_md;
int is_async;
} grpc_fake_oauth2_credentials;
/* -- Fake Oauth2 credentials. -- */
static void fake_oauth2_destroy(grpc_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;
}
/* -- 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. -- */
static void fake_transport_security_credentials_destroy(
@ -944,12 +940,6 @@ grpc_server_credentials *grpc_fake_transport_security_server_credentials_create(
/* -- Composite credentials. -- */
typedef struct {
grpc_credentials base;
grpc_credentials_array inner;
grpc_credentials *connector_creds;
} grpc_composite_credentials;
typedef struct {
grpc_composite_credentials *composite_creds;
size_t creds_index;
@ -1180,11 +1170,6 @@ grpc_credentials *grpc_credentials_contains_type(
/* -- IAM credentials. -- */
typedef struct {
grpc_credentials base;
grpc_credentials_md_store *iam_md;
} grpc_iam_credentials;
static void iam_destroy(grpc_credentials *creds) {
grpc_iam_credentials *c = (grpc_iam_credentials *)creds;
grpc_credentials_md_store_unref(c->iam_md);

@ -39,6 +39,8 @@
#include <grpc/grpc_security.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"
struct grpc_httpcli_response;
@ -178,11 +180,22 @@ grpc_credentials_status
grpc_oauth2_token_fetcher_credentials_parse_server_response(
const struct grpc_httpcli_response *response,
grpc_credentials_md_store **token_md, gpr_timespec *token_lifetime);
void grpc_flush_cached_google_default_credentials(void);
/* Simulates an oauth2 token fetch with the specified value for testing. */
grpc_credentials *grpc_fake_oauth2_credentials_create(
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. --- */
typedef struct {
@ -199,4 +212,103 @@ struct grpc_server_credentials {
grpc_security_status grpc_server_credentials_create_security_connector(
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 */

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

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

@ -37,10 +37,16 @@
#include <grpc/support/slice.h>
#include <openssl/rsa.h>
#include "src/core/json/json.h"
/* --- Constants. --- */
#define GRPC_JWT_OAUTH2_AUDIENCE "https://www.googleapis.com/oauth2/v3/token"
#define GRPC_AUTH_JSON_TYPE_INVALID "invalid"
#define GRPC_AUTH_JSON_TYPE_SERVICE_ACCOUNT "service_account"
#define GRPC_AUTH_JSON_TYPE_AUTHORIZED_USER "authorized_user"
/* --- auth_json_key parsing. --- */
typedef struct {
@ -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(
const char *json_string);
/* Creates a json_key object from parsed json. Returns an invalid object if a
parsing error has been encountered. */
grpc_auth_json_key grpc_auth_json_key_create_from_json(const grpc_json *json);
/* Destructs the object. */
void grpc_auth_json_key_destruct(grpc_auth_json_key *json_key);
@ -97,6 +107,11 @@ int grpc_auth_refresh_token_is_valid(
grpc_auth_refresh_token grpc_auth_refresh_token_create_from_string(
const char *json_string);
/* Creates a refresh token object from parsed json. Returns an invalid object if
a parsing error has been encountered. */
grpc_auth_refresh_token grpc_auth_refresh_token_create_from_json(
const grpc_json *json);
/* Destructs the object. */
void grpc_auth_refresh_token_destruct(grpc_auth_refresh_token *refresh_token);

@ -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 */

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

@ -36,6 +36,10 @@
#include "src/core/security/credentials.h"
#ifdef __cplusplus
extern "C" {
#endif
/* --- grpc_auth_context ---
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);
void grpc_server_security_context_destroy(void *ctx);
#ifdef __cplusplus
}
#endif
#endif /* GRPC_INTERNAL_CORE_SECURITY_SECURITY_CONTEXT_H */

@ -157,7 +157,7 @@ static void record_stats(census_ht* store, census_op_id op_id,
key.ptr = gpr_strdup(key.ptr);
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 {
census_internal_unlock_trace_store();
}
@ -185,7 +185,7 @@ static void get_stats(census_ht* store, census_aggregated_rpc_stats* data) {
if (store != NULL) {
size_t n;
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);
if (kv != NULL) {
data->num_entries = n;

@ -94,7 +94,7 @@ census_op_id census_tracing_start_op(void) {
g_id++;
memcpy(&ret->id, &g_id, sizeof(census_op_id));
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);
gpr_log(GPR_DEBUG, "Start tracing for id %lu", g_id);
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));
if (trace != NULL) {
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;
const char* s = anno_txt;
@ -143,8 +143,8 @@ void census_tracing_end_op(census_op_id op_id) {
gpr_mu_lock(&g_mu);
trace = census_ht_find(g_trace_store, op_id_as_key(&op_id));
if (trace != NULL) {
trace->rpc_stats.elapsed_time_ms =
gpr_timespec_to_micros(gpr_time_sub(gpr_now(), trace->ts));
trace->rpc_stats.elapsed_time_ms = gpr_timespec_to_micros(
gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME), trace->ts));
gpr_log(GPR_DEBUG, "End tracing for id %lu, method %s, latency %f us",
op_id_2_uint64(&op_id), trace->method,
trace->rpc_stats.elapsed_time_ms);
@ -194,8 +194,8 @@ const char* census_get_trace_method_name(const census_trace_obj* trace) {
static census_trace_annotation* dup_annotation_chain(
census_trace_annotation* from) {
census_trace_annotation *ret = NULL;
census_trace_annotation **to = &ret;
census_trace_annotation* ret = NULL;
census_trace_annotation** to = &ret;
for (; from != NULL; from = from->next) {
*to = gpr_malloc(sizeof(census_trace_annotation));
memcpy(*to, from, sizeof(census_trace_annotation));
@ -223,9 +223,9 @@ census_trace_obj** census_get_active_ops(int* num_active_ops) {
size_t n = 0;
census_ht_kv* all_kvs = census_ht_get_all_elements(g_trace_store, &n);
*num_active_ops = (int)n;
if (n != 0 ) {
if (n != 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++) {
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.
stat.latency = 0.153;
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
result[kMinInterval].statistic = &sums[kMinInterval];
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",
result[kMinInterval].count,
(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. */
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 {
gpr_event ev;
gpr_event_init(&ev);
gpr_event_wait(&ev,
gpr_time_add(gpr_now(), gpr_time_from_micros(1000)));
gpr_event_wait(&ev, gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
gpr_time_from_micros(1000)));
}
}
} while (failures != 0);

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

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

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

@ -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 */

@ -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) {
SleepConditionVariableCS(cv, &mu->cs, INFINITE);
} 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 deadline_ms =
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;
}
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;
clock_gettime(CLOCK_REALTIME, &now);
clock_gettime(clockid_for_gpr_clock[clock], &now);
return gpr_from_timespec(now);
}
#else
/* For some reason Apple's OSes haven't implemented clock_gettime. */
#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;
struct timeval now_tv;
gettimeofday(&now_tv, NULL);
now.tv_sec = now_tv.tv_sec;
now.tv_nsec = now_tv.tv_usec * 1000;
double now_dbl;
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;
}
#endif
@ -83,7 +113,7 @@ void gpr_sleep_until(gpr_timespec until) {
for (;;) {
/* We could simplify by using clock_nanosleep instead, but it might be
* slightly less portable. */
now = gpr_now();
now = gpr_now(GPR_CLOCK_REALTIME);
if (gpr_time_cmp(until, now) <= 0) {
return;
}

@ -40,12 +40,34 @@
#include <grpc/support/time.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;
struct _timeb now_tb;
_ftime_s(&now_tb);
now_tv.tv_sec = now_tb.time;
now_tv.tv_nsec = now_tb.millitm * 1000000;
LARGE_INTEGER timestamp;
double now_dbl;
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;
}
@ -57,13 +79,14 @@ void gpr_sleep_until(gpr_timespec until) {
for (;;) {
/* We could simplify by using clock_nanosleep instead, but it might be
* slightly less portable. */
now = gpr_now();
now = gpr_now(GPR_CLOCK_REALTIME);
if (gpr_time_cmp(until, now) <= 0) {
return;
}
delta = gpr_time_sub(until, now);
sleep_millis = (DWORD)delta.tv_sec * GPR_MS_PER_SEC + delta.tv_nsec / GPR_NS_PER_MS;
sleep_millis =
(DWORD)delta.tv_sec * GPR_MS_PER_SEC + delta.tv_nsec / GPR_NS_PER_MS;
Sleep(sleep_millis);
}
}

@ -49,6 +49,17 @@
#include <stdlib.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 {
@ -135,6 +146,7 @@ struct grpc_call {
grpc_mdctx *metadata_context;
/* TODO(ctiller): share with cq if possible? */
gpr_mu mu;
gpr_mu completion_mu;
/* how far through the stream have we read? */
read_state read_state;
@ -162,6 +174,8 @@ struct grpc_call {
gpr_uint8 error_status_set;
/** should the alarm be cancelled */
gpr_uint8 cancel_alarm;
/** bitmask of allocated completion events in completions */
gpr_uint8 allocated_completions;
/* flags with bits corresponding to write states allowing us to determine
what was sent */
@ -250,6 +264,9 @@ struct grpc_call {
grpc_iomgr_closure on_done_recv;
grpc_iomgr_closure on_done_send;
grpc_iomgr_closure on_done_bind;
/** completion events - for completion queue use */
grpc_cq_completion completions[MAX_CONCURRENT_COMPLETIONS];
};
#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);
memset(call, 0, sizeof(grpc_call));
gpr_mu_init(&call->mu);
gpr_mu_init(&call->completion_mu);
call->channel = channel;
call->cq = cq;
if (cq) {
@ -349,6 +367,29 @@ grpc_completion_queue *grpc_call_get_completion_queue(grpc_call *call) {
return call->cq;
}
static grpc_cq_completion *allocate_completion(grpc_call *call) {
gpr_uint8 i;
gpr_mu_lock(&call->completion_mu);
for (i = 0; i < GPR_ARRAY_SIZE(call->completions); i++) {
if (call->allocated_completions & (1u << i)) {
continue;
}
call->allocated_completions |= 1u << i;
gpr_mu_unlock(&call->completion_mu);
return &call->completions[i];
}
gpr_log(GPR_ERROR, "should never reach here");
abort();
}
static void done_completion(void *call, grpc_cq_completion *completion) {
grpc_call *c = call;
gpr_mu_lock(&c->completion_mu);
c->allocated_completions &= ~(1u << (completion - c->completions));
gpr_mu_unlock(&c->completion_mu);
GRPC_CALL_INTERNAL_UNREF(c, "completion", 1);
}
#ifdef GRPC_CALL_REF_COUNT_DEBUG
void grpc_call_internal_ref(grpc_call *c, const char *reason) {
gpr_log(GPR_DEBUG, "CALL: ref %p %d -> %d [%s]", c,
@ -365,20 +406,21 @@ static void destroy_call(void *call, int ignored_success) {
grpc_call_stack_destroy(CALL_STACK_FROM_CALL(c));
GRPC_CHANNEL_INTERNAL_UNREF(c->channel, "call");
gpr_mu_destroy(&c->mu);
gpr_mu_destroy(&c->completion_mu);
for (i = 0; i < STATUS_SOURCE_COUNT; i++) {
if (c->status[i].details) {
grpc_mdstr_unref(c->status[i].details);
GRPC_MDSTR_UNREF(c->status[i].details);
}
}
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);
for (i = 0; i < GPR_ARRAY_SIZE(c->buffered_metadata); i++) {
gpr_free(c->buffered_metadata[i].metadata);
}
for (i = 0; i < c->send_initial_metadata_count; i++) {
grpc_mdelem_unref(c->send_initial_metadata[i].md);
GRPC_MDELEM_UNREF(c->send_initial_metadata[i].md);
}
for (i = 0; i < GRPC_CONTEXT_COUNT; i++) {
if (c->context[i].destroy) {
@ -435,7 +477,7 @@ static void set_decode_compression_level(grpc_call *call,
static void set_status_details(grpc_call *call, status_source source,
grpc_mdstr *status) {
if (call->status[source].details != NULL) {
grpc_mdstr_unref(call->status[source].details);
GRPC_MDSTR_UNREF(call->status[source].details);
}
call->status[source].details = status;
}
@ -614,7 +656,7 @@ static void finish_live_ioreq_op(grpc_call *call, grpc_ioreq_op op,
case GRPC_IOREQ_SEND_STATUS:
if (call->request_data[GRPC_IOREQ_SEND_STATUS].send_status.details !=
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 =
NULL;
@ -943,7 +985,7 @@ static int fill_send_ops(grpc_call *call, grpc_transport_stream_op *op) {
&mdb, &call->details_link,
grpc_mdelem_from_metadata_strings(
call->metadata_context,
grpc_mdstr_ref(
GRPC_MDSTR_REF(
grpc_channel_get_message_string(call->channel)),
data.send_status.details));
call->request_data[GRPC_IOREQ_SEND_STATUS].send_status.details =
@ -1051,7 +1093,7 @@ static grpc_call_error start_ioreq(grpc_call *call, const grpc_ioreq *reqs,
reqs[i].data.send_status.code);
if (reqs[i].data.send_status.details) {
set_status_details(call, STATUS_FROM_SERVER_STATUS,
grpc_mdstr_ref(reqs[i].data.send_status.details));
GRPC_MDSTR_REF(reqs[i].data.send_status.details));
}
}
have_ops |= 1u << op;
@ -1188,7 +1230,8 @@ static void set_deadline_alarm(grpc_call *call, gpr_timespec deadline) {
}
GRPC_CALL_INTERNAL_REF(call, "alarm");
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
@ -1255,7 +1298,7 @@ static void recv_metadata(grpc_call *call, grpc_metadata_batch *md) {
if (key == grpc_channel_get_status_string(call->channel)) {
set_status_code(call, STATUS_FROM_WIRE, decode_status(md));
} else if (key == grpc_channel_get_message_string(call->channel)) {
set_status_details(call, STATUS_FROM_WIRE, grpc_mdstr_ref(md->value));
set_status_details(call, STATUS_FROM_WIRE, GRPC_MDSTR_REF(md->value));
} else if (key ==
grpc_channel_get_compresssion_level_string(call->channel)) {
set_decode_compression_level(call, decode_compression(md));
@ -1291,10 +1334,10 @@ static void recv_metadata(grpc_call *call, grpc_metadata_batch *md) {
grpc_mdctx_lock(mdctx);
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) {
grpc_mdctx_locked_mdelem_unref(mdctx, l->md);
GRPC_MDCTX_LOCKED_MDELEM_UNREF(mdctx, l->md);
}
grpc_mdctx_unlock(mdctx);
}
@ -1316,11 +1359,13 @@ static void set_cancelled_value(grpc_status_code status, void *dest) {
}
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) {
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) {
@ -1343,8 +1388,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);
if (nops == 0) {
grpc_cq_begin_op(call->cq, call);
grpc_cq_end_op(call->cq, tag, call, 1);
grpc_cq_begin_op(call->cq);
GRPC_CALL_INTERNAL_REF(call, "completion");
grpc_cq_end_op(call->cq, tag, 1, done_completion, call,
allocate_completion(call));
return GRPC_CALL_OK;
}
@ -1466,7 +1513,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);
}

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

@ -45,34 +45,20 @@
#include <grpc/support/atm.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 */
struct grpc_completion_queue {
/* When refs drops to zero, we are in shutdown mode, and will be destroyable
once all queued events are drained */
gpr_refcount refs;
/* Once owning_refs drops to zero, we will destroy the cq */
/** completed events */
grpc_cq_completion completed_head;
grpc_cq_completion *completed_tail;
/** Number of pending events (+1 if we're not shutdown) */
gpr_refcount pending_events;
/** Once owning_refs drops to zero, we will destroy the cq */
gpr_refcount owning_refs;
/* the set of low level i/o things that concern this cq */
/** the set of low level i/o things that concern this cq */
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_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;
};
@ -80,19 +66,20 @@ grpc_completion_queue *grpc_completion_queue_create(void) {
grpc_completion_queue *cc = gpr_malloc(sizeof(grpc_completion_queue));
memset(cc, 0, sizeof(*cc));
/* 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 */
gpr_ref_init(&cc->owning_refs, 2);
grpc_pollset_init(&cc->pollset);
cc->completed_tail = &cc->completed_head;
cc->completed_head.next = (gpr_uintptr)cc->completed_tail;
return cc;
}
#ifdef GRPC_CQ_REF_COUNT_DEBUG
void grpc_cq_internal_ref(grpc_completion_queue *cc, const char *reason,
const char *file, int line) {
gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "CQ:%p ref %d -> %d %s",
cc, (int)cc->owning_refs.count, (int)cc->owning_refs.count + 1,
reason);
gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "CQ:%p ref %d -> %d %s", cc,
(int)cc->owning_refs.count, (int)cc->owning_refs.count + 1, reason);
#else
void grpc_cq_internal_ref(grpc_completion_queue *cc) {
#endif
@ -107,186 +94,135 @@ static void on_pollset_destroy_done(void *arg) {
#ifdef GRPC_CQ_REF_COUNT_DEBUG
void grpc_cq_internal_unref(grpc_completion_queue *cc, const char *reason,
const char *file, int line) {
gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "CQ:%p unref %d -> %d %s",
cc, (int)cc->owning_refs.count, (int)cc->owning_refs.count - 1,
reason);
gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "CQ:%p unref %d -> %d %s", cc,
(int)cc->owning_refs.count, (int)cc->owning_refs.count - 1, reason);
#else
void grpc_cq_internal_unref(grpc_completion_queue *cc) {
#endif
if (gpr_unref(&cc->owning_refs)) {
GPR_ASSERT(cc->queue == NULL);
GPR_ASSERT(cc->completed_head.next == (gpr_uintptr)&cc->completed_head);
grpc_pollset_destroy(&cc->pollset);
gpr_free(cc);
}
}
/* Create and append an event to the queue. Returns the event so that its data
members can be filled in.
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");
void grpc_cq_begin_op(grpc_completion_queue *cc) {
gpr_ref(&cc->pending_events);
}
/* Signal the end of an operation - if this is the last waiting-to-be-queued
event, then enter shutdown mode */
void grpc_cq_end_op(grpc_completion_queue *cc, void *tag, grpc_call *call,
int success) {
event *ev;
int shutdown = 0;
gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset));
ev = add_locked(cc, GRPC_OP_COMPLETE, tag, call);
ev->base.success = success;
if (gpr_unref(&cc->refs)) {
/* Queue a GRPC_OP_COMPLETED operation */
void grpc_cq_end_op(grpc_completion_queue *cc, void *tag, int success,
void (*done)(void *done_arg, grpc_cq_completion *storage),
void *done_arg, grpc_cq_completion *storage) {
int shutdown = gpr_unref(&cc->pending_events);
storage->tag = tag;
storage->done = done;
storage->done_arg = done_arg;
storage->next =
((gpr_uintptr)&cc->completed_head) | ((gpr_uintptr)(success != 0));
if (!shutdown) {
gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset));
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 {
gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset));
cc->completed_tail->next =
((gpr_uintptr)storage) | (1u & (gpr_uintptr)cc->completed_tail->next);
cc->completed_tail = storage;
GPR_ASSERT(!cc->shutdown);
GPR_ASSERT(cc->shutdown_called);
cc->shutdown = 1;
shutdown = 1;
}
gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset));
if (call) GRPC_CALL_INTERNAL_UNREF(call, "cq", 0);
if (shutdown) {
gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset));
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,
gpr_timespec deadline) {
event *ev = NULL;
grpc_event ret;
GRPC_CQ_INTERNAL_REF(cc, "next");
gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset));
for (;;) {
if (cc->queue != NULL) {
gpr_uintptr bucket;
ev = cc->queue;
bucket = ((gpr_uintptr)ev->base.tag) % NUM_TAG_BUCKETS;
cc->queue = ev->queue_next;
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;
if (cc->completed_tail != &cc->completed_head) {
grpc_cq_completion *c = (grpc_cq_completion *)cc->completed_head.next;
cc->completed_head.next = c->next & ~(gpr_uintptr)1;
if (c == cc->completed_tail) {
cc->completed_tail = &cc->completed_head;
}
gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset));
ret.type = GRPC_OP_COMPLETE;
ret.success = c->next & 1u;
ret.tag = c->tag;
c->done(c->done_arg, c);
break;
}
if (cc->shutdown) {
ev = create_shutdown_event();
gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset));
memset(&ret, 0, sizeof(ret));
ret.type = GRPC_QUEUE_SHUTDOWN;
break;
}
if (!grpc_pollset_work(&cc->pollset, deadline)) {
gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset));
memset(&ret, 0, sizeof(ret));
ret.type = GRPC_QUEUE_TIMEOUT;
GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ret);
GRPC_CQ_INTERNAL_UNREF(cc, "next");
return ret;
break;
}
}
gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset));
ret = ev->base;
gpr_free(ev);
GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ret);
GRPC_CQ_INTERNAL_UNREF(cc, "next");
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,
gpr_timespec deadline) {
event *ev = NULL;
grpc_event ret;
grpc_cq_completion *c;
grpc_cq_completion *prev;
GRPC_CQ_INTERNAL_REF(cc, "pluck");
gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset));
for (;;) {
if ((ev = pluck_event(cc, tag))) {
break;
prev = &cc->completed_head;
while ((c = (grpc_cq_completion *)(prev->next & ~(gpr_uintptr)1)) !=
&cc->completed_head) {
if (c->tag == tag) {
prev->next =
(prev->next & (gpr_uintptr)1) | (c->next & ~(gpr_uintptr)1);
if (c == cc->completed_tail) {
cc->completed_tail = prev;
}
gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset));
ret.type = GRPC_OP_COMPLETE;
ret.success = c->next & 1u;
ret.tag = c->tag;
c->done(c->done_arg, c);
goto done;
}
prev = c;
}
if (cc->shutdown) {
ev = create_shutdown_event();
gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset));
memset(&ret, 0, sizeof(ret));
ret.type = GRPC_QUEUE_SHUTDOWN;
break;
}
if (!grpc_pollset_work(&cc->pollset, deadline)) {
gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset));
memset(&ret, 0, sizeof(ret));
ret.type = GRPC_QUEUE_TIMEOUT;
GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ret);
GRPC_CQ_INTERNAL_UNREF(cc, "pluck");
return ret;
break;
}
}
gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset));
ret = ev->base;
gpr_free(ev);
done:
GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ret);
GRPC_CQ_INTERNAL_UNREF(cc, "pluck");
return ret;
@ -303,7 +239,7 @@ void grpc_completion_queue_shutdown(grpc_completion_queue *cc) {
cc->shutdown_called = 1;
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_ASSERT(!cc->shutdown);
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) {
gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset));
grpc_pollset_kick(&cc->pollset);
grpc_pollset_work(&cc->pollset,
gpr_time_add(gpr_now(), gpr_time_from_millis(100)));
grpc_pollset_work(&cc->pollset, gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
gpr_time_from_millis(100)));
gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset));
}

@ -39,6 +39,17 @@
#include "src/core/iomgr/pollset.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
void grpc_cq_internal_ref(grpc_completion_queue *cc, const char *reason,
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
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 */
void grpc_cq_end_op(grpc_completion_queue *cc, void *tag, grpc_call *call,
int success);
void grpc_cq_end_op(grpc_completion_queue *cc, void *tag, int success,
void (*done)(void *done_arg, grpc_cq_completion *storage),
void *done_arg, grpc_cq_completion *storage);
grpc_pollset *grpc_cq_pollset(grpc_completion_queue *cc);

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

@ -72,12 +72,14 @@ typedef struct {
typedef enum { BATCH_CALL, REGISTERED_CALL } requested_call_type;
typedef struct {
typedef struct requested_call {
requested_call_type type;
struct requested_call *next;
void *tag;
grpc_completion_queue *cq_bound_to_call;
grpc_completion_queue *cq_for_notification;
grpc_call **call;
grpc_cq_completion completion;
union {
struct {
grpc_call_details *details;
@ -92,17 +94,11 @@ typedef struct {
} data;
} requested_call;
typedef struct {
requested_call *calls;
size_t count;
size_t capacity;
} requested_call_array;
struct registered_method {
char *method;
char *host;
call_data *pending;
requested_call_array requested;
requested_call *requests;
registered_method *next;
};
@ -131,6 +127,7 @@ struct channel_data {
typedef struct shutdown_tag {
void *tag;
grpc_completion_queue *cq;
grpc_cq_completion completion;
} shutdown_tag;
struct grpc_server {
@ -153,7 +150,7 @@ struct grpc_server {
gpr_mu mu_call; /* mutex for call-specific state */
registered_method *registered_methods;
requested_call_array requested_calls;
requested_call *requests;
gpr_uint8 shutdown;
gpr_uint8 shutdown_published;
@ -166,6 +163,9 @@ struct grpc_server {
listener *listeners;
int listeners_destroyed;
gpr_refcount internal_refcount;
/** when did we print the last shutdown progress message */
gpr_timespec last_shutdown_message_time;
};
typedef enum {
@ -182,7 +182,11 @@ typedef enum {
struct call_data {
grpc_call *call;
/** protects state */
gpr_mu mu_state;
/** the current state of a call - see call_state */
call_state state;
grpc_mdstr *path;
grpc_mdstr *host;
gpr_timespec deadline;
@ -266,7 +270,8 @@ static void send_shutdown(grpc_channel *channel, int send_goaway,
}
static void channel_broadcaster_shutdown(channel_broadcaster *cb,
int send_goaway, int force_disconnect) {
int send_goaway,
int force_disconnect) {
size_t i;
for (i = 0; i < cb->num_channels; i++) {
@ -325,22 +330,6 @@ static int call_list_remove(call_data *call, call_list list) {
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) {
gpr_ref(&server->internal_refcount);
}
@ -352,12 +341,10 @@ static void server_delete(grpc_server *server) {
gpr_mu_destroy(&server->mu_global);
gpr_mu_destroy(&server->mu_call);
gpr_free(server->channel_filters);
requested_call_array_destroy(&server->requested_calls);
while ((rm = server->registered_methods) != NULL) {
server->registered_methods = rm->next;
gpr_free(rm->method);
gpr_free(rm->host);
requested_call_array_destroy(&rm->requested);
gpr_free(rm);
}
for (i = 0; i < server->cq_count; i++) {
@ -403,21 +390,26 @@ static void destroy_channel(channel_data *chand) {
grpc_iomgr_add_callback(&chand->finish_destroy_channel_closure);
}
static void finish_start_new_rpc_and_unlock(grpc_server *server,
grpc_call_element *elem,
call_data **pending_root,
requested_call_array *array) {
requested_call rc;
static void finish_start_new_rpc(grpc_server *server, grpc_call_element *elem,
call_data **pending_root,
requested_call **requests) {
requested_call *rc;
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;
gpr_mu_unlock(&calld->mu_state);
call_list_join(pending_root, calld, PENDING_START);
gpr_mu_unlock(&server->mu_call);
} else {
rc = array->calls[--array->count];
*requests = rc->next;
gpr_mu_lock(&calld->mu_state);
calld->state = ACTIVATED;
gpr_mu_unlock(&calld->mu_state);
gpr_mu_unlock(&server->mu_call);
begin_call(server, calld, &rc);
begin_call(server, calld, rc);
}
}
@ -429,20 +421,18 @@ static void start_new_rpc(grpc_call_element *elem) {
gpr_uint32 hash;
channel_registered_method *rm;
gpr_mu_lock(&server->mu_call);
if (chand->registered_methods && calld->path && calld->host) {
/* TODO(ctiller): unify these two searches */
/* check for an exact match with host */
hash = GRPC_MDSTR_KV_HASH(calld->host->hash, calld->path->hash);
for (i = 0; i < chand->registered_method_max_probes; i++) {
for (i = 0; i <= chand->registered_method_max_probes; i++) {
rm = &chand->registered_methods[(hash + i) %
chand->registered_method_slots];
if (!rm) break;
if (rm->host != calld->host) continue;
if (rm->method != calld->path) continue;
finish_start_new_rpc_and_unlock(server, elem,
&rm->server_registered_method->pending,
&rm->server_registered_method->requested);
finish_start_new_rpc(server, elem, &rm->server_registered_method->pending,
&rm->server_registered_method->requests);
return;
}
/* check for a wildcard method definition (no host set) */
@ -453,14 +443,13 @@ static void start_new_rpc(grpc_call_element *elem) {
if (!rm) break;
if (rm->host != NULL) continue;
if (rm->method != calld->path) continue;
finish_start_new_rpc_and_unlock(server, elem,
&rm->server_registered_method->pending,
&rm->server_registered_method->requested);
finish_start_new_rpc(server, elem, &rm->server_registered_method->pending,
&rm->server_registered_method->requests);
return;
}
}
finish_start_new_rpc_and_unlock(server, elem, &server->lists[PENDING_START],
&server->requested_calls);
finish_start_new_rpc(server, elem, &server->lists[PENDING_START],
&server->requests);
}
static void kill_zombie(void *elem, int success) {
@ -476,26 +465,47 @@ static int num_listeners(grpc_server *server) {
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) {
size_t i;
if (!server->shutdown || server->shutdown_published) {
return;
}
if (server->root_channel_data.next != &server->root_channel_data) {
gpr_log(GPR_DEBUG,
"Waiting for all channels to close before destroying 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));
if (server->root_channel_data.next != &server->root_channel_data ||
server->listeners_destroyed < num_listeners(server)) {
if (gpr_time_cmp(
gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME), server->last_shutdown_message_time),
gpr_time_from_seconds(1)) >= 0) {
server->last_shutdown_message_time = gpr_now(GPR_CLOCK_REALTIME);
gpr_log(GPR_DEBUG,
"Waiting for %d channels and %d/%d listeners to be destroyed"
" before shutting down server",
num_channels(server),
num_listeners(server) - server->listeners_destroyed,
num_listeners(server));
}
return;
}
server->shutdown_published = 1;
for (i = 0; i < server->num_shutdown_tags; i++) {
grpc_cq_end_op(server->shutdown_tags[i].cq, server->shutdown_tags[i].tag,
NULL, 1);
server_ref(server);
grpc_cq_end_op(server->shutdown_tags[i].cq, server->shutdown_tags[i].tag, 1,
done_shutdown_event, server,
&server->shutdown_tags[i].completion);
}
}
@ -504,10 +514,10 @@ static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) {
channel_data *chand = elem->channel_data;
call_data *calld = elem->call_data;
if (md->key == chand->path_key) {
calld->path = grpc_mdstr_ref(md->value);
calld->path = GRPC_MDSTR_REF(md->value);
return NULL;
} else if (md->key == chand->authority_key) {
calld->host = grpc_mdstr_ref(md->value);
calld->host = GRPC_MDSTR_REF(md->value);
return NULL;
}
return md;
@ -541,27 +551,34 @@ static void server_on_recv(void *ptr, int success) {
case GRPC_STREAM_SEND_CLOSED:
break;
case GRPC_STREAM_RECV_CLOSED:
gpr_mu_lock(&chand->server->mu_call);
gpr_mu_lock(&calld->mu_state);
if (calld->state == NOT_STARTED) {
calld->state = ZOMBIED;
gpr_mu_unlock(&calld->mu_state);
grpc_iomgr_closure_init(&calld->kill_zombie_closure, kill_zombie, elem);
grpc_iomgr_add_callback(&calld->kill_zombie_closure);
} else {
gpr_mu_unlock(&calld->mu_state);
}
gpr_mu_unlock(&chand->server->mu_call);
break;
case GRPC_STREAM_CLOSED:
gpr_mu_lock(&chand->server->mu_call);
gpr_mu_lock(&calld->mu_state);
if (calld->state == NOT_STARTED) {
calld->state = ZOMBIED;
gpr_mu_unlock(&calld->mu_state);
grpc_iomgr_closure_init(&calld->kill_zombie_closure, kill_zombie, elem);
grpc_iomgr_add_callback(&calld->kill_zombie_closure);
} else if (calld->state == PENDING) {
call_list_remove(calld, PENDING_START);
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_add_callback(&calld->kill_zombie_closure);
} else {
gpr_mu_unlock(&calld->mu_state);
}
gpr_mu_unlock(&chand->server->mu_call);
break;
}
@ -623,6 +640,7 @@ static void init_call_elem(grpc_call_element *elem,
memset(calld, 0, sizeof(call_data));
calld->deadline = gpr_inf_future;
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);
@ -634,21 +652,22 @@ static void init_call_elem(grpc_call_element *elem,
static void destroy_call_elem(grpc_call_element *elem) {
channel_data *chand = elem->channel_data;
call_data *calld = elem->call_data;
size_t i;
gpr_mu_lock(&chand->server->mu_call);
for (i = 0; i < CALL_LIST_COUNT; i++) {
call_list_remove(elem->call_data, i);
if (calld->state == PENDING) {
gpr_mu_lock(&chand->server->mu_call);
call_list_remove(elem->call_data, PENDING_START);
gpr_mu_unlock(&chand->server->mu_call);
}
gpr_mu_unlock(&chand->server->mu_call);
if (calld->host) {
grpc_mdstr_unref(calld->host);
GRPC_MDSTR_UNREF(calld->host);
}
if (calld->path) {
grpc_mdstr_unref(calld->path);
GRPC_MDSTR_UNREF(calld->path);
}
gpr_mu_destroy(&calld->mu_state);
server_unref(chand->server);
}
@ -676,10 +695,10 @@ static void destroy_channel_elem(grpc_channel_element *elem) {
if (chand->registered_methods) {
for (i = 0; i < chand->registered_method_slots; i++) {
if (chand->registered_methods[i].method) {
grpc_mdstr_unref(chand->registered_methods[i].method);
GRPC_MDSTR_UNREF(chand->registered_methods[i].method);
}
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);
@ -691,8 +710,8 @@ static void destroy_channel_elem(grpc_channel_element *elem) {
chand->next = chand->prev = chand;
maybe_finish_shutdown(chand->server);
gpr_mu_unlock(&chand->server->mu_global);
grpc_mdstr_unref(chand->path_key);
grpc_mdstr_unref(chand->authority_key);
GRPC_MDSTR_UNREF(chand->path_key);
GRPC_MDSTR_UNREF(chand->authority_key);
server_unref(chand->server);
}
}
@ -910,15 +929,14 @@ void grpc_server_setup_transport(grpc_server *s, grpc_transport *transport,
void grpc_server_shutdown_and_notify(grpc_server *server,
grpc_completion_queue *cq, void *tag) {
listener *l;
requested_call_array requested_calls;
size_t i;
requested_call *requests = NULL;
registered_method *rm;
shutdown_tag *sdt;
channel_broadcaster broadcaster;
/* lock, and gather up some stuff to do */
gpr_mu_lock(&server->mu_global);
grpc_cq_begin_op(cq, NULL);
grpc_cq_begin_op(cq);
server->shutdown_tags =
gpr_realloc(server->shutdown_tags,
sizeof(shutdown_tag) * (server->num_shutdown_tags + 1));
@ -930,27 +948,21 @@ void grpc_server_shutdown_and_notify(grpc_server *server,
return;
}
server->last_shutdown_message_time = gpr_now(GPR_CLOCK_REALTIME);
channel_broadcaster_init(server, &broadcaster);
/* collect all unregistered then registered calls */
gpr_mu_lock(&server->mu_call);
requested_calls = server->requested_calls;
memset(&server->requested_calls, 0, sizeof(server->requested_calls));
requests = server->requests;
server->requests = NULL;
for (rm = server->registered_methods; rm; rm = rm->next) {
if (requested_calls.count + rm->requested.count >
requested_calls.capacity) {
requested_calls.capacity =
GPR_MAX(requested_calls.count + rm->requested.count,
2 * requested_calls.capacity);
requested_calls.calls =
gpr_realloc(requested_calls.calls, sizeof(*requested_calls.calls) *
requested_calls.capacity);
while (rm->requests != NULL) {
requested_call *c = rm->requests;
rm->requests = c->next;
c->next = requests;
requests = c;
}
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);
@ -959,10 +971,11 @@ void grpc_server_shutdown_and_notify(grpc_server *server,
gpr_mu_unlock(&server->mu_global);
/* terminate all the requested calls */
for (i = 0; i < requested_calls.count; i++) {
fail_call(server, &requested_calls.calls[i]);
while (requests != NULL) {
requested_call *next = requests->next;
fail_call(server, requests);
requests = next;
}
gpr_free(requested_calls.calls);
/* Shutdown listeners */
for (l = server->listeners; l; l = l->next) {
@ -1024,7 +1037,7 @@ void grpc_server_add_listener(grpc_server *server, void *arg,
static grpc_call_error queue_call_request(grpc_server *server,
requested_call *rc) {
call_data *calld = NULL;
requested_call_array *requested_calls = NULL;
requested_call **requests = NULL;
gpr_mu_lock(&server->mu_call);
if (server->shutdown) {
gpr_mu_unlock(&server->mu_call);
@ -1035,22 +1048,25 @@ static grpc_call_error queue_call_request(grpc_server *server,
case BATCH_CALL:
calld =
call_list_remove_head(&server->lists[PENDING_START], PENDING_START);
requested_calls = &server->requested_calls;
requests = &server->requests;
break;
case REGISTERED_CALL:
calld = call_list_remove_head(
&rc->data.registered.registered_method->pending, PENDING_START);
requested_calls = &rc->data.registered.registered_method->requested;
requests = &rc->data.registered.registered_method->requests;
break;
}
if (calld) {
if (calld != NULL) {
gpr_mu_unlock(&server->mu_call);
gpr_mu_lock(&calld->mu_state);
GPR_ASSERT(calld->state == PENDING);
calld->state = ACTIVATED;
gpr_mu_unlock(&server->mu_call);
gpr_mu_unlock(&calld->mu_state);
begin_call(server, calld, rc);
return GRPC_CALL_OK;
} else {
*requested_call_array_add(requested_calls) = *rc;
rc->next = *requests;
*requests = rc;
gpr_mu_unlock(&server->mu_call);
return GRPC_CALL_OK;
}
@ -1061,22 +1077,23 @@ grpc_call_error grpc_server_request_call(
grpc_metadata_array *initial_metadata,
grpc_completion_queue *cq_bound_to_call,
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,
initial_metadata, cq_bound_to_call,
cq_for_notification, tag);
if (!grpc_cq_is_server_cq(cq_for_notification)) {
gpr_free(rc);
return GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE;
}
grpc_cq_begin_op(cq_for_notification, NULL);
rc.type = BATCH_CALL;
rc.tag = tag;
rc.cq_bound_to_call = cq_bound_to_call;
rc.cq_for_notification = cq_for_notification;
rc.call = call;
rc.data.batch.details = details;
rc.data.batch.initial_metadata = initial_metadata;
return queue_call_request(server, &rc);
grpc_cq_begin_op(cq_for_notification);
rc->type = BATCH_CALL;
rc->tag = tag;
rc->cq_bound_to_call = cq_bound_to_call;
rc->cq_for_notification = cq_for_notification;
rc->call = call;
rc->data.batch.details = details;
rc->data.batch.initial_metadata = initial_metadata;
return queue_call_request(server, rc);
}
grpc_call_error grpc_server_request_registered_call(
@ -1084,22 +1101,23 @@ grpc_call_error grpc_server_request_registered_call(
grpc_metadata_array *initial_metadata, grpc_byte_buffer **optional_payload,
grpc_completion_queue *cq_bound_to_call,
grpc_completion_queue *cq_for_notification, void *tag) {
requested_call rc;
requested_call *rc = gpr_malloc(sizeof(*rc));
registered_method *registered_method = rm;
if (!grpc_cq_is_server_cq(cq_for_notification)) {
gpr_free(rc);
return GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE;
}
grpc_cq_begin_op(cq_for_notification, NULL);
rc.type = REGISTERED_CALL;
rc.tag = tag;
rc.cq_bound_to_call = cq_bound_to_call;
rc.cq_for_notification = cq_for_notification;
rc.call = call;
rc.data.registered.registered_method = registered_method;
rc.data.registered.deadline = deadline;
rc.data.registered.initial_metadata = initial_metadata;
rc.data.registered.optional_payload = optional_payload;
return queue_call_request(server, &rc);
grpc_cq_begin_op(cq_for_notification);
rc->type = REGISTERED_CALL;
rc->tag = tag;
rc->cq_bound_to_call = cq_bound_to_call;
rc->cq_for_notification = cq_for_notification;
rc->call = call;
rc->data.registered.registered_method = registered_method;
rc->data.registered.deadline = deadline;
rc->data.registered.initial_metadata = initial_metadata;
rc->data.registered.optional_payload = optional_payload;
return queue_call_request(server, rc);
}
static void publish_registered_or_batch(grpc_call *call, int success,
@ -1166,8 +1184,11 @@ static void begin_call(grpc_server *server, call_data *calld,
}
GRPC_CALL_INTERNAL_REF(calld->call, "server");
grpc_call_start_ioreq_and_call_back(calld->call, req, r - req, publish,
rc->tag);
grpc_call_start_ioreq_and_call_back(calld->call, req, r - req, publish, rc);
}
static void done_request_event(void *req, grpc_cq_completion *c) {
gpr_free(req);
}
static void fail_call(grpc_server *server, requested_call *rc) {
@ -1180,15 +1201,19 @@ static void fail_call(grpc_server *server, requested_call *rc) {
rc->data.registered.initial_metadata->count = 0;
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,
void *tag) {
void *prc) {
grpc_call_element *elem =
grpc_call_stack_element(grpc_call_get_call_stack(call), 0);
requested_call *rc = prc;
call_data *calld = elem->call_data;
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) {

@ -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:
case GRPC_CHTTP2_DATA_FH_0:
p->frame_type = *cur;
if (++cur == end) {
p->state = GRPC_CHTTP2_DATA_FH_1;
return GRPC_CHTTP2_PARSE_OK;
}
switch (p->frame_type) {
case 0:
/* noop */
break;
case 1:
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);
return GRPC_CHTTP2_STREAM_ERROR;
}
if (++cur == end) {
p->state = GRPC_CHTTP2_DATA_FH_1;
return GRPC_CHTTP2_PARSE_OK;
}
/* fallthrough */
case GRPC_CHTTP2_DATA_FH_1:
p->frame_size = ((gpr_uint32)*cur) << 24;

@ -622,7 +622,7 @@ static const gpr_uint8 inverse_base64[256] = {
static void on_hdr(grpc_chttp2_hpack_parser *p, grpc_mdelem *md,
int add_to_table) {
if (add_to_table) {
grpc_mdelem_ref(md);
GRPC_MDELEM_REF(md);
grpc_chttp2_hptbl_add(&p->table, 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,
const gpr_uint8 *cur, const gpr_uint8 *end) {
grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
grpc_mdelem_ref(md);
GRPC_MDELEM_REF(md);
on_hdr(p, md, 0);
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) {
grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
grpc_mdstr_ref(md->key),
GRPC_MDSTR_REF(md->key),
take_string(p, &p->value)),
1);
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) {
grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
grpc_mdstr_ref(md->key),
GRPC_MDSTR_REF(md->key),
take_string(p, &p->value)),
0);
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) {
grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
grpc_mdstr_ref(md->key),
GRPC_MDSTR_REF(md->key),
take_string(p, &p->value)),
0);
return parse_begin(p, cur, end);
@ -1336,7 +1336,7 @@ static void on_header_not_set(void *user_data, grpc_mdelem *md) {
valuehex);
gpr_free(keyhex);
gpr_free(valuehex);
grpc_mdelem_unref(md);
GRPC_MDELEM_UNREF(md);
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) {
size_t 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++) {
grpc_mdelem_unref(
GRPC_MDELEM_UNREF(
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;
tbl->first_ent = (tbl->first_ent + 1) % GRPC_CHTTP2_MAX_TABLE_COUNT;
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) {

@ -49,7 +49,7 @@ void grpc_chttp2_incoming_metadata_buffer_destroy(
grpc_chttp2_incoming_metadata_buffer *buffer) {
size_t i;
for (i = 0; i < buffer->count; i++) {
grpc_mdelem_unref(buffer->elems[i].md);
GRPC_MDELEM_UNREF(buffer->elems[i].md);
}
gpr_free(buffer->elems);
}

@ -205,7 +205,8 @@ void grpc_chttp2_publish_reads(
}
if (stream_parsing->saw_rst_stream) {
stream_global->cancelled = 1;
stream_global->cancelled_status = grpc_chttp2_http2_error_to_grpc_status(stream_parsing->rst_stream_reason);
stream_global->cancelled_status = grpc_chttp2_http2_error_to_grpc_status(
stream_parsing->rst_stream_reason);
if (stream_parsing->rst_stream_reason == GRPC_CHTTP2_NO_ERROR) {
stream_global->published_cancelled = 1;
}
@ -463,7 +464,7 @@ static grpc_chttp2_parse_error skip_parser(
return GRPC_CHTTP2_PARSE_OK;
}
static void skip_header(void *tp, grpc_mdelem *md) { grpc_mdelem_unref(md); }
static void skip_header(void *tp, grpc_mdelem *md) { GRPC_MDELEM_UNREF(md); }
static int init_skip_frame_parser(
grpc_chttp2_transport_parsing *transport_parsing, int is_header) {
@ -599,8 +600,8 @@ static void on_header(void *tp, grpc_mdelem *md) {
}
grpc_chttp2_incoming_metadata_buffer_set_deadline(
&stream_parsing->incoming_metadata,
gpr_time_add(gpr_now(), *cached_timeout));
grpc_mdelem_unref(md);
gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), *cached_timeout));
GRPC_MDELEM_UNREF(md);
} else {
grpc_chttp2_incoming_metadata_buffer_add(&stream_parsing->incoming_metadata,
md);

@ -247,19 +247,19 @@ static grpc_mdelem *add_elem(grpc_chttp2_hpack_compressor *c,
} else if (c->entries_keys[HASH_FRAGMENT_3(key_hash)] == elem->key) {
c->indices_keys[HASH_FRAGMENT_3(key_hash)] = new_index;
} else if (c->entries_keys[HASH_FRAGMENT_2(key_hash)] == NULL) {
c->entries_keys[HASH_FRAGMENT_2(key_hash)] = grpc_mdstr_ref(elem->key);
c->entries_keys[HASH_FRAGMENT_2(key_hash)] = GRPC_MDSTR_REF(elem->key);
c->indices_keys[HASH_FRAGMENT_2(key_hash)] = new_index;
} else if (c->entries_keys[HASH_FRAGMENT_3(key_hash)] == NULL) {
c->entries_keys[HASH_FRAGMENT_3(key_hash)] = grpc_mdstr_ref(elem->key);
c->entries_keys[HASH_FRAGMENT_3(key_hash)] = GRPC_MDSTR_REF(elem->key);
c->indices_keys[HASH_FRAGMENT_3(key_hash)] = new_index;
} else if (c->indices_keys[HASH_FRAGMENT_2(key_hash)] <
c->indices_keys[HASH_FRAGMENT_3(key_hash)]) {
grpc_mdstr_unref(c->entries_keys[HASH_FRAGMENT_2(key_hash)]);
c->entries_keys[HASH_FRAGMENT_2(key_hash)] = grpc_mdstr_ref(elem->key);
GRPC_MDSTR_UNREF(c->entries_keys[HASH_FRAGMENT_2(key_hash)]);
c->entries_keys[HASH_FRAGMENT_2(key_hash)] = GRPC_MDSTR_REF(elem->key);
c->indices_keys[HASH_FRAGMENT_2(key_hash)] = new_index;
} else {
grpc_mdstr_unref(c->entries_keys[HASH_FRAGMENT_3(key_hash)]);
c->entries_keys[HASH_FRAGMENT_3(key_hash)] = grpc_mdstr_ref(elem->key);
GRPC_MDSTR_UNREF(c->entries_keys[HASH_FRAGMENT_3(key_hash)]);
c->entries_keys[HASH_FRAGMENT_3(key_hash)] = GRPC_MDSTR_REF(elem->key);
c->indices_keys[HASH_FRAGMENT_3(key_hash)] = new_index;
}
@ -437,12 +437,13 @@ static void deadline_enc(grpc_chttp2_hpack_compressor *c, gpr_timespec deadline,
framer_state *st) {
char timeout_str[GRPC_CHTTP2_TIMEOUT_ENCODE_MIN_BUFSIZE];
grpc_mdelem *mdelem;
grpc_chttp2_encode_timeout(gpr_time_sub(deadline, gpr_now()), timeout_str);
grpc_chttp2_encode_timeout(
gpr_time_sub(deadline, gpr_now(GPR_CLOCK_REALTIME)), timeout_str);
mdelem = grpc_mdelem_from_metadata_strings(
c->mdctx, grpc_mdstr_ref(c->timeout_key_str),
c->mdctx, GRPC_MDSTR_REF(c->timeout_key_str),
grpc_mdstr_from_string(c->mdctx, timeout_str));
mdelem = hpack_enc(c, mdelem, st);
if (mdelem) grpc_mdelem_unref(mdelem);
if (mdelem) GRPC_MDELEM_UNREF(mdelem);
}
gpr_slice grpc_chttp2_data_frame_create_empty_close(gpr_uint32 id) {
@ -461,10 +462,10 @@ void grpc_chttp2_hpack_compressor_init(grpc_chttp2_hpack_compressor *c,
void grpc_chttp2_hpack_compressor_destroy(grpc_chttp2_hpack_compressor *c) {
int i;
for (i = 0; i < GRPC_CHTTP2_HPACKC_NUM_VALUES; i++) {
if (c->entries_keys[i]) grpc_mdstr_unref(c->entries_keys[i]);
if (c->entries_elems[i]) grpc_mdelem_unref(c->entries_elems[i]);
if (c->entries_keys[i]) GRPC_MDSTR_UNREF(c->entries_keys[i]);
if (c->entries_elems[i]) GRPC_MDELEM_UNREF(c->entries_elems[i]);
}
grpc_mdstr_unref(c->timeout_key_str);
GRPC_MDSTR_UNREF(c->timeout_key_str);
}
gpr_uint32 grpc_chttp2_preencode(grpc_stream_op *inops, size_t *inops_count,
@ -620,10 +621,10 @@ void grpc_chttp2_encode(grpc_stream_op *ops, size_t ops_count, int eof,
op = &ops[unref_op];
if (op->type != GRPC_OP_METADATA) continue;
for (l = op->data.metadata.list.head; l; l = l->next) {
if (l->md) grpc_mdctx_locked_mdelem_unref(mdctx, l->md);
if (l->md) GRPC_MDCTX_LOCKED_MDELEM_UNREF(mdctx, l->md);
}
for (l = op->data.metadata.garbage.head; l; l = l->next) {
grpc_mdctx_locked_mdelem_unref(mdctx, l->md);
GRPC_MDCTX_LOCKED_MDELEM_UNREF(mdctx, l->md);
}
}
grpc_mdctx_unlock(mdctx);

@ -139,7 +139,7 @@ static void destruct_transport(grpc_chttp2_transport *t) {
grpc_chttp2_hpack_parser_destroy(&t->parsing.hpack_parser);
grpc_chttp2_goaway_parser_destroy(&t->parsing.goaway_parser);
grpc_mdstr_unref(t->parsing.str_grpc_timeout);
GRPC_MDSTR_UNREF(t->parsing.str_grpc_timeout);
for (i = 0; i < STREAM_LIST_COUNT; i++) {
GPR_ASSERT(t->lists[i].head == NULL);
@ -876,11 +876,19 @@ static void update_global_window(void *args, gpr_uint32 id, void *stream) {
grpc_chttp2_stream *s = stream;
grpc_chttp2_transport_global *transport_global = &t->global;
grpc_chttp2_stream_global *stream_global = &s->global;
int was_zero;
int is_zero;
GRPC_CHTTP2_FLOWCTL_TRACE_STREAM("settings", transport_global, stream_global,
outgoing_window,
t->parsing.initial_window_update);
was_zero = stream_global->outgoing_window <= 0;
stream_global->outgoing_window += t->parsing.initial_window_update;
is_zero = stream_global->outgoing_window <= 0;
if (was_zero && !is_zero) {
grpc_chttp2_list_add_writable_stream(transport_global, stream_global);
}
}
static void read_error_locked(grpc_chttp2_transport *t) {

@ -48,6 +48,20 @@
#define INITIAL_STRTAB_CAPACITY 4
#define INITIAL_MDTAB_CAPACITY 4
#ifdef GRPC_METADATA_REFCOUNT_DEBUG
#define DEBUG_ARGS , const char *file, int line
#define FWD_DEBUG_ARGS , file, line
#define INTERNAL_STRING_REF(s) internal_string_ref((s), __FILE__, __LINE__)
#define INTERNAL_STRING_UNREF(s) internal_string_unref((s), __FILE__, __LINE__)
#define REF_MD_LOCKED(s) ref_md_locked((s), __FILE__, __LINE__)
#else
#define DEBUG_ARGS
#define FWD_DEBUG_ARGS
#define INTERNAL_STRING_REF(s) internal_string_ref((s))
#define INTERNAL_STRING_UNREF(s) internal_string_unref((s))
#define REF_MD_LOCKED(s) ref_md_locked((s))
#endif
typedef struct internal_string {
/* must be byte compatible with grpc_mdstr */
gpr_slice slice;
@ -73,6 +87,7 @@ typedef struct internal_metadata {
gpr_atm refcnt;
/* private only data */
gpr_mu mu_user_data;
void *user_data;
void (*destroy_user_data)(void *user_data);
@ -96,8 +111,8 @@ struct grpc_mdctx {
size_t mdtab_capacity;
};
static void internal_string_ref(internal_string *s);
static void internal_string_unref(internal_string *s);
static void internal_string_ref(internal_string *s DEBUG_ARGS);
static void internal_string_unref(internal_string *s DEBUG_ARGS);
static void discard_metadata(grpc_mdctx *ctx);
static void gc_mdtab(grpc_mdctx *ctx);
static void metadata_context_destroy_locked(grpc_mdctx *ctx);
@ -132,7 +147,15 @@ static void unlock(grpc_mdctx *ctx) {
gpr_mu_unlock(&ctx->mu);
}
static void ref_md_locked(internal_metadata *md) {
static void ref_md_locked(internal_metadata *md DEBUG_ARGS) {
#ifdef GRPC_METADATA_REFCOUNT_DEBUG
gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
"ELM REF:%p:%d->%d: '%s' = '%s'", md,
gpr_atm_no_barrier_load(&md->refcnt),
gpr_atm_no_barrier_load(&md->refcnt) + 1,
grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
#endif
if (0 == gpr_atm_no_barrier_fetch_add(&md->refcnt, 1)) {
md->context->mdtab_free--;
}
@ -161,7 +184,7 @@ grpc_mdctx *grpc_mdctx_create(void) {
/* This seed is used to prevent remote connections from controlling hash table
* collisions. It needs to be somewhat unpredictable to a remote connection.
*/
return grpc_mdctx_create_with_seed(gpr_now().tv_nsec);
return grpc_mdctx_create_with_seed(gpr_now(GPR_CLOCK_REALTIME).tv_nsec);
}
static void discard_metadata(grpc_mdctx *ctx) {
@ -173,11 +196,12 @@ static void discard_metadata(grpc_mdctx *ctx) {
while (cur) {
GPR_ASSERT(gpr_atm_acq_load(&cur->refcnt) == 0);
next = cur->bucket_next;
internal_string_unref(cur->key);
internal_string_unref(cur->value);
INTERNAL_STRING_UNREF(cur->key);
INTERNAL_STRING_UNREF(cur->value);
if (cur->user_data) {
cur->destroy_user_data(cur->user_data);
}
gpr_mu_destroy(&cur->mu_user_data);
gpr_free(cur);
cur = next;
ctx->mdtab_free--;
@ -248,9 +272,19 @@ static void internal_destroy_string(internal_string *is) {
gpr_free(is);
}
static void internal_string_ref(internal_string *s) { ++s->refs; }
static void internal_string_ref(internal_string *s DEBUG_ARGS) {
#ifdef GRPC_METADATA_REFCOUNT_DEBUG
gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "STR REF:%p:%d->%d: '%s'", s,
s->refs, s->refs + 1, grpc_mdstr_as_c_string((grpc_mdstr *)s));
#endif
++s->refs;
}
static void internal_string_unref(internal_string *s) {
static void internal_string_unref(internal_string *s DEBUG_ARGS) {
#ifdef GRPC_METADATA_REFCOUNT_DEBUG
gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "STR UNREF:%p:%d->%d: '%s'", s,
s->refs, s->refs - 1, grpc_mdstr_as_c_string((grpc_mdstr *)s));
#endif
GPR_ASSERT(s->refs > 0);
if (0 == --s->refs) {
internal_destroy_string(s);
@ -262,7 +296,7 @@ static void slice_ref(void *p) {
(internal_string *)((char *)p - offsetof(internal_string, refcount));
grpc_mdctx *ctx = is->context;
lock(ctx);
internal_string_ref(is);
INTERNAL_STRING_REF(is);
unlock(ctx);
}
@ -271,7 +305,7 @@ static void slice_unref(void *p) {
(internal_string *)((char *)p - offsetof(internal_string, refcount));
grpc_mdctx *ctx = is->context;
lock(ctx);
internal_string_unref(is);
INTERNAL_STRING_UNREF(is);
unlock(ctx);
}
@ -297,7 +331,7 @@ grpc_mdstr *grpc_mdstr_from_buffer(grpc_mdctx *ctx, const gpr_uint8 *buf,
for (s = ctx->strtab[hash % ctx->strtab_capacity]; s; s = s->bucket_next) {
if (s->hash == hash && GPR_SLICE_LENGTH(s->slice) == length &&
0 == memcmp(buf, GPR_SLICE_START_PTR(s->slice), length)) {
internal_string_ref(s);
INTERNAL_STRING_REF(s);
unlock(ctx);
return (grpc_mdstr *)s;
}
@ -353,8 +387,8 @@ static void gc_mdtab(grpc_mdctx *ctx) {
for (md = ctx->mdtab[i]; md; md = next) {
next = md->bucket_next;
if (gpr_atm_acq_load(&md->refcnt) == 0) {
internal_string_unref(md->key);
internal_string_unref(md->value);
INTERNAL_STRING_UNREF(md->key);
INTERNAL_STRING_UNREF(md->value);
if (md->user_data) {
md->destroy_user_data(md->user_data);
}
@ -418,9 +452,9 @@ grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_mdctx *ctx,
/* search for an existing pair */
for (md = ctx->mdtab[hash % ctx->mdtab_capacity]; md; md = md->bucket_next) {
if (md->key == key && md->value == value) {
ref_md_locked(md);
internal_string_unref(key);
internal_string_unref(value);
REF_MD_LOCKED(md);
INTERNAL_STRING_UNREF(key);
INTERNAL_STRING_UNREF(value);
unlock(ctx);
return (grpc_mdelem *)md;
}
@ -435,6 +469,13 @@ grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_mdctx *ctx,
md->user_data = NULL;
md->destroy_user_data = NULL;
md->bucket_next = ctx->mdtab[hash % ctx->mdtab_capacity];
gpr_mu_init(&md->mu_user_data);
#ifdef GRPC_METADATA_REFCOUNT_DEBUG
gpr_log(GPR_DEBUG, "ELM NEW:%p:%d: '%s' = '%s'", md,
gpr_atm_no_barrier_load(&md->refcnt),
grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
#endif
ctx->mdtab[hash % ctx->mdtab_capacity] = md;
ctx->mdtab_count++;
@ -469,8 +510,16 @@ grpc_mdelem *grpc_mdelem_from_string_and_buffer(grpc_mdctx *ctx,
grpc_mdstr_from_buffer(ctx, value, value_length));
}
grpc_mdelem *grpc_mdelem_ref(grpc_mdelem *gmd) {
grpc_mdelem *grpc_mdelem_ref(grpc_mdelem *gmd DEBUG_ARGS) {
internal_metadata *md = (internal_metadata *)gmd;
#ifdef GRPC_METADATA_REFCOUNT_DEBUG
gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
"ELM REF:%p:%d->%d: '%s' = '%s'", md,
gpr_atm_no_barrier_load(&md->refcnt),
gpr_atm_no_barrier_load(&md->refcnt) + 1,
grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
#endif
/* we can assume the ref count is >= 1 as the application is calling
this function - meaning that no adjustment to mdtab_free is necessary,
simplifying the logic here to be just an atomic increment */
@ -480,10 +529,18 @@ grpc_mdelem *grpc_mdelem_ref(grpc_mdelem *gmd) {
return gmd;
}
void grpc_mdelem_unref(grpc_mdelem *gmd) {
void grpc_mdelem_unref(grpc_mdelem *gmd DEBUG_ARGS) {
internal_metadata *md = (internal_metadata *)gmd;
grpc_mdctx *ctx = md->context;
lock(ctx);
#ifdef GRPC_METADATA_REFCOUNT_DEBUG
gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
"ELM UNREF:%p:%d->%d: '%s' = '%s'", md,
gpr_atm_no_barrier_load(&md->refcnt),
gpr_atm_no_barrier_load(&md->refcnt) - 1,
grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
#endif
assert(gpr_atm_no_barrier_load(&md->refcnt) >= 1);
if (1 == gpr_atm_full_fetch_add(&md->refcnt, -1)) {
ctx->mdtab_free++;
@ -495,20 +552,20 @@ const char *grpc_mdstr_as_c_string(grpc_mdstr *s) {
return (const char *)GPR_SLICE_START_PTR(s->slice);
}
grpc_mdstr *grpc_mdstr_ref(grpc_mdstr *gs) {
grpc_mdstr *grpc_mdstr_ref(grpc_mdstr *gs DEBUG_ARGS) {
internal_string *s = (internal_string *)gs;
grpc_mdctx *ctx = s->context;
lock(ctx);
internal_string_ref(s);
internal_string_ref(s FWD_DEBUG_ARGS);
unlock(ctx);
return gs;
}
void grpc_mdstr_unref(grpc_mdstr *gs) {
void grpc_mdstr_unref(grpc_mdstr *gs DEBUG_ARGS) {
internal_string *s = (internal_string *)gs;
grpc_mdctx *ctx = s->context;
lock(ctx);
internal_string_unref(s);
internal_string_unref(s FWD_DEBUG_ARGS);
unlock(ctx);
}
@ -527,18 +584,29 @@ size_t grpc_mdctx_get_mdtab_free_test_only(grpc_mdctx *ctx) {
void *grpc_mdelem_get_user_data(grpc_mdelem *md,
void (*if_destroy_func)(void *)) {
internal_metadata *im = (internal_metadata *)md;
return im->destroy_user_data == if_destroy_func ? im->user_data : NULL;
void *result;
gpr_mu_lock(&im->mu_user_data);
result = im->destroy_user_data == if_destroy_func ? im->user_data : NULL;
gpr_mu_unlock(&im->mu_user_data);
return result;
}
void grpc_mdelem_set_user_data(grpc_mdelem *md, void (*destroy_func)(void *),
void *user_data) {
internal_metadata *im = (internal_metadata *)md;
GPR_ASSERT((user_data == NULL) == (destroy_func == NULL));
gpr_mu_lock(&im->mu_user_data);
if (im->destroy_user_data) {
im->destroy_user_data(im->user_data);
/* user data can only be set once */
gpr_mu_unlock(&im->mu_user_data);
if (destroy_func != NULL) {
destroy_func(user_data);
}
return;
}
im->destroy_user_data = destroy_func;
im->user_data = user_data;
gpr_mu_unlock(&im->mu_user_data);
}
gpr_slice grpc_mdstr_as_base64_encoded_and_huffman_compressed(grpc_mdstr *gs) {
@ -558,10 +626,19 @@ gpr_slice grpc_mdstr_as_base64_encoded_and_huffman_compressed(grpc_mdstr *gs) {
void grpc_mdctx_lock(grpc_mdctx *ctx) { lock(ctx); }
void grpc_mdctx_locked_mdelem_unref(grpc_mdctx *ctx, grpc_mdelem *gmd) {
void grpc_mdctx_locked_mdelem_unref(grpc_mdctx *ctx,
grpc_mdelem *gmd DEBUG_ARGS) {
internal_metadata *md = (internal_metadata *)gmd;
grpc_mdctx *elem_ctx = md->context;
GPR_ASSERT(ctx == elem_ctx);
#ifdef GRPC_METADATA_REFCOUNT_DEBUG
gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
"ELM UNREF:%p:%d->%d: '%s' = '%s'", md,
gpr_atm_no_barrier_load(&md->refcnt),
gpr_atm_no_barrier_load(&md->refcnt) - 1,
grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
#endif
assert(gpr_atm_no_barrier_load(&md->refcnt) >= 1);
if (1 == gpr_atm_full_fetch_add(&md->refcnt, -1)) {
ctx->mdtab_free++;

@ -127,11 +127,25 @@ void grpc_mdelem_set_user_data(grpc_mdelem *md, void (*destroy_func)(void *),
void *user_data);
/* Reference counting */
#ifdef GRPC_METADATA_REFCOUNT_DEBUG
#define GRPC_MDSTR_REF(s) grpc_mdstr_ref((s), __FILE__, __LINE__)
#define GRPC_MDSTR_UNREF(s) grpc_mdstr_unref((s), __FILE__, __LINE__)
#define GRPC_MDELEM_REF(s) grpc_mdelem_ref((s), __FILE__, __LINE__)
#define GRPC_MDELEM_UNREF(s) grpc_mdelem_unref((s), __FILE__, __LINE__)
grpc_mdstr *grpc_mdstr_ref(grpc_mdstr *s, const char *file, int line);
void grpc_mdstr_unref(grpc_mdstr *s, const char *file, int line);
grpc_mdelem *grpc_mdelem_ref(grpc_mdelem *md, const char *file, int line);
void grpc_mdelem_unref(grpc_mdelem *md, const char *file, int line);
#else
#define GRPC_MDSTR_REF(s) grpc_mdstr_ref((s))
#define GRPC_MDSTR_UNREF(s) grpc_mdstr_unref((s))
#define GRPC_MDELEM_REF(s) grpc_mdelem_ref((s))
#define GRPC_MDELEM_UNREF(s) grpc_mdelem_unref((s))
grpc_mdstr *grpc_mdstr_ref(grpc_mdstr *s);
void grpc_mdstr_unref(grpc_mdstr *s);
grpc_mdelem *grpc_mdelem_ref(grpc_mdelem *md);
void grpc_mdelem_unref(grpc_mdelem *md);
#endif
/* Recover a char* from a grpc_mdstr. The returned string is null terminated.
Does not promise that the returned string has no embedded nulls however. */
@ -147,8 +161,18 @@ int grpc_mdstr_is_bin_suffixed(grpc_mdstr *s);
/* Lock the metadata context: it's only safe to call _locked_ functions against
this context from the calling thread until grpc_mdctx_unlock is called */
void grpc_mdctx_lock(grpc_mdctx *ctx);
#ifdef GRPC_METADATA_REFCOUNT_DEBUG
#define GRPC_MDCTX_LOCKED_MDELEM_UNREF(ctx, elem) \
grpc_mdctx_locked_mdelem_unref((ctx), (elem), __FILE__, __LINE__)
/* Unref a metadata element */
void grpc_mdctx_locked_mdelem_unref(grpc_mdctx *ctx, grpc_mdelem *elem,
const char *file, int line);
#else
#define GRPC_MDCTX_LOCKED_MDELEM_UNREF(ctx, elem) \
grpc_mdctx_locked_mdelem_unref((ctx), (elem))
/* Unref a metadata element */
void grpc_mdctx_locked_mdelem_unref(grpc_mdctx *ctx, grpc_mdelem *elem);
#endif
/* Unlock the metadata context */
void grpc_mdctx_unlock(grpc_mdctx *ctx);

@ -211,10 +211,10 @@ void grpc_metadata_batch_init(grpc_metadata_batch *batch) {
void grpc_metadata_batch_destroy(grpc_metadata_batch *batch) {
grpc_linked_mdelem *l;
for (l = batch->list.head; l; l = l->next) {
grpc_mdelem_unref(l->md);
GRPC_MDELEM_UNREF(l->md);
}
for (l = batch->garbage.head; l; l = l->next) {
grpc_mdelem_unref(l->md);
GRPC_MDELEM_UNREF(l->md);
}
}
@ -315,7 +315,7 @@ void grpc_metadata_batch_filter(grpc_metadata_batch *batch,
assert_valid_list(&batch->list);
link_head(&batch->garbage, l);
} else if (filt != orig) {
grpc_mdelem_unref(orig);
GRPC_MDELEM_UNREF(orig);
l->md = filt;
}
}

@ -85,6 +85,6 @@ void grpc_transport_stream_op_add_cancellation(grpc_transport_stream_op *op,
op->cancel_with_status = status;
}
if (message) {
grpc_mdstr_unref(message);
GRPC_MDSTR_UNREF(message);
}
}

@ -60,7 +60,7 @@ Channel::~Channel() { grpc_channel_destroy(c_channel_); }
Call Channel::CreateCall(const RpcMethod& method, ClientContext* context,
CompletionQueue* cq) {
auto c_call =
method.channel_tag()
method.channel_tag() && context->authority().empty()
? grpc_channel_create_registered_call(c_channel_, cq->cq(),
method.channel_tag(),
context->raw_deadline())

@ -36,6 +36,7 @@
#include <grpc/grpc.h>
#include <grpc++/credentials.h>
#include <grpc++/time.h>
#include "src/cpp/common/create_auth_context.h"
namespace grpc {
@ -75,6 +76,13 @@ void ClientContext::set_call(grpc_call* call,
}
}
std::shared_ptr<const AuthContext> ClientContext::auth_context() const {
if (auth_context_.get() == nullptr) {
auth_context_ = CreateAuthContext(call_);
}
return auth_context_;
}
void ClientContext::TryCancel() {
if (call_) {
grpc_call_cancel(call_);

@ -117,6 +117,13 @@ std::shared_ptr<Credentials> RefreshTokenCredentials(
grpc_refresh_token_credentials_create(json_refresh_token.c_str()));
}
// Builds access token credentials.
std::shared_ptr<Credentials> AccessTokenCredentials(
const grpc::string& access_token) {
return WrapCredentials(
grpc_access_token_credentials_create(access_token.c_str()));
}
// Builds IAM credentials.
std::shared_ptr<Credentials> IAMCredentials(
const grpc::string& authorization_token,

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

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

@ -0,0 +1,80 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "src/cpp/common/secure_auth_context.h"
#include <grpc/grpc_security.h>
namespace grpc {
SecureAuthContext::SecureAuthContext(grpc_auth_context* ctx) : ctx_(ctx) {}
SecureAuthContext::~SecureAuthContext() { grpc_auth_context_release(ctx_); }
std::vector<grpc::string> SecureAuthContext::GetPeerIdentity() const {
if (!ctx_) {
return std::vector<grpc::string>();
}
grpc_auth_property_iterator iter = grpc_auth_context_peer_identity(ctx_);
std::vector<grpc::string> identity;
const grpc_auth_property* property = nullptr;
while ((property = grpc_auth_property_iterator_next(&iter))) {
identity.push_back(grpc::string(property->value, property->value_length));
}
return identity;
}
grpc::string SecureAuthContext::GetPeerIdentityPropertyName() const {
if (!ctx_) {
return "";
}
const char* name = grpc_auth_context_peer_identity_property_name(ctx_);
return name == nullptr ? "" : name;
}
std::vector<grpc::string> SecureAuthContext::FindPropertyValues(
const grpc::string& name) const {
if (!ctx_) {
return std::vector<grpc::string>();
}
grpc_auth_property_iterator iter =
grpc_auth_context_find_properties_by_name(ctx_, name.c_str());
const grpc_auth_property* property = nullptr;
std::vector<grpc::string> values;
while ((property = grpc_auth_property_iterator_next(&iter))) {
values.push_back(grpc::string(property->value, property->value_length));
}
return values;
}
} // namespace grpc

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

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

@ -1,99 +0,0 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <grpc++/async_server_context.h>
#include <grpc/grpc.h>
#include <grpc/support/log.h>
#include "src/cpp/proto/proto_utils.h"
#include <grpc++/config.h>
#include <grpc++/status.h>
namespace grpc {
AsyncServerContext::AsyncServerContext(
grpc_call* call, const grpc::string& method, const grpc::string& host,
system_clock::time_point absolute_deadline)
: method_(method),
host_(host),
absolute_deadline_(absolute_deadline),
request_(nullptr),
call_(call) {}
AsyncServerContext::~AsyncServerContext() { grpc_call_destroy(call_); }
void AsyncServerContext::Accept(grpc_completion_queue* cq) {
GPR_ASSERT(grpc_call_server_accept_old(call_, cq, this) == GRPC_CALL_OK);
GPR_ASSERT(grpc_call_server_end_initial_metadata_old(
call_, GRPC_WRITE_BUFFER_HINT) == GRPC_CALL_OK);
}
bool AsyncServerContext::StartRead(grpc::protobuf::Message* request) {
GPR_ASSERT(request);
request_ = request;
grpc_call_error err = grpc_call_start_read_old(call_, this);
return err == GRPC_CALL_OK;
}
bool AsyncServerContext::StartWrite(const grpc::protobuf::Message& response,
int flags) {
grpc_byte_buffer* buffer = nullptr;
GRPC_TIMER_MARK(SER_PROTO_BEGIN, call_->call());
if (!SerializeProto(response, &buffer)) {
return false;
}
GRPC_TIMER_MARK(SER_PROTO_END, call_->call());
grpc_call_error err = grpc_call_start_write_old(call_, buffer, this, flags);
grpc_byte_buffer_destroy(buffer);
return err == GRPC_CALL_OK;
}
bool AsyncServerContext::StartWriteStatus(const Status& status) {
grpc_call_error err = grpc_call_start_write_status_old(
call_, static_cast<grpc_status_code>(status.code()),
status.details().empty() ? nullptr
: const_cast<char*>(status.details().c_str()),
this);
return err == GRPC_CALL_OK;
}
bool AsyncServerContext::ParseRead(grpc_byte_buffer* read_buffer) {
GPR_ASSERT(request_);
GRPC_TIMER_MARK(DESER_PROTO_BEGIN, call_->call());
bool success = DeserializeProto(read_buffer, request_);
GRPC_TIMER_MARK(DESER_PROTO_END, call_->call());
request_ = nullptr;
return success;
}
} // namespace grpc

@ -118,7 +118,7 @@ class Server::SyncRequest GRPC_FINAL : public CompletionQueueTag {
has_request_payload_(mrd->has_request_payload_),
request_payload_(mrd->request_payload_),
method_(mrd->method_) {
ctx_.call_ = mrd->call_;
ctx_.set_call(mrd->call_);
ctx_.cq_ = &cq_;
GPR_ASSERT(mrd->in_flight_);
mrd->in_flight_ = false;
@ -207,10 +207,11 @@ Server::~Server() {
delete sync_methods_;
}
bool Server::RegisterService(RpcService* service) {
bool Server::RegisterService(const grpc::string *host, RpcService* service) {
for (int i = 0; i < service->GetMethodCount(); ++i) {
RpcServiceMethod* method = service->GetMethod(i);
void* tag = grpc_server_register_method(server_, method->name(), nullptr);
void* tag = grpc_server_register_method(
server_, method->name(), host ? host->c_str() : nullptr);
if (!tag) {
gpr_log(GPR_DEBUG, "Attempt to register %s multiple times",
method->name());
@ -222,14 +223,14 @@ bool Server::RegisterService(RpcService* service) {
return true;
}
bool Server::RegisterAsyncService(AsynchronousService* service) {
bool Server::RegisterAsyncService(const grpc::string *host, AsynchronousService* service) {
GPR_ASSERT(service->server_ == nullptr &&
"Can only register an asynchronous service against one server.");
service->server_ = this;
service->request_args_ = new void*[service->method_count_];
for (size_t i = 0; i < service->method_count_; ++i) {
void* tag = grpc_server_register_method(server_, service->method_names_[i],
nullptr);
host ? host->c_str() : nullptr);
if (!tag) {
gpr_log(GPR_DEBUG, "Attempt to register %s multiple times",
service->method_names_[i]);
@ -325,7 +326,7 @@ bool Server::BaseAsyncRequest::FinalizeResult(void** tag, bool* status) {
}
}
grpc_metadata_array_destroy(&initial_metadata_array_);
context_->call_ = call_;
context_->set_call(call_);
context_->cq_ = call_cq_;
Call call(call_, server_, call_cq_, server_->max_message_size_);
if (*status && call_) {

@ -51,11 +51,21 @@ std::unique_ptr<ServerCompletionQueue> ServerBuilder::AddCompletionQueue() {
}
void ServerBuilder::RegisterService(SynchronousService* service) {
services_.push_back(service->service());
services_.emplace_back(new NamedService<RpcService>(service->service()));
}
void ServerBuilder::RegisterAsyncService(AsynchronousService* service) {
async_services_.push_back(service);
async_services_.emplace_back(new NamedService<AsynchronousService>(service));
}
void ServerBuilder::RegisterService(
const grpc::string& addr, SynchronousService* service) {
services_.emplace_back(new NamedService<RpcService>(addr, service->service()));
}
void ServerBuilder::RegisterAsyncService(
const grpc::string& addr, AsynchronousService* service) {
async_services_.emplace_back(new NamedService<AsynchronousService>(addr, service));
}
void ServerBuilder::RegisterAsyncGenericService(AsyncGenericService* service) {
@ -97,13 +107,13 @@ std::unique_ptr<Server> ServerBuilder::BuildAndStart() {
}
for (auto service = services_.begin(); service != services_.end();
service++) {
if (!server->RegisterService(*service)) {
if (!server->RegisterService((*service)->host.get(), (*service)->service)) {
return nullptr;
}
}
for (auto service = async_services_.begin();
service != async_services_.end(); service++) {
if (!server->RegisterAsyncService(*service)) {
if (!server->RegisterAsyncService((*service)->host.get(), (*service)->service)) {
return nullptr;
}
}

@ -39,6 +39,8 @@
#include <grpc++/impl/sync.h>
#include <grpc++/time.h>
#include "src/cpp/common/create_auth_context.h"
namespace grpc {
// CompletionOp
@ -146,4 +148,16 @@ bool ServerContext::IsCancelled() {
return completion_op_ && completion_op_->CheckCancelled(cq_);
}
void ServerContext::set_call(grpc_call* call) {
call_ = call;
auth_context_ = CreateAuthContext(call);
}
std::shared_ptr<const AuthContext> ServerContext::auth_context() const {
if (auth_context_.get() == nullptr) {
auth_context_ = CreateAuthContext(call_);
}
return auth_context_;
}
} // namespace grpc

@ -68,6 +68,9 @@
<Reference Include="System.Net.Http.WebRequest" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Grpc.Core\Version.cs">
<Link>Version.cs</Link>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="GoogleCredential.cs" />
<Compile Include="OAuth2InterceptorFactory.cs" />
@ -81,6 +84,7 @@
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="Grpc.Auth.nuspec" />
<None Include="packages.config" />
</ItemGroup>
<Import Project="..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" />

@ -5,19 +5,19 @@
<title>gRPC C# Auth</title>
<summary>Auth library for C# implementation of gRPC - an RPC library and framework</summary>
<description>Auth library for C# implementation of gRPC - an RPC library and framework. See project site for more info.</description>
<version>0.5.1</version>
<version>$version$</version>
<authors>Google Inc.</authors>
<owners>grpc-packages</owners>
<licenseUrl>https://github.com/grpc/grpc/blob/master/LICENSE</licenseUrl>
<projectUrl>https://github.com/grpc/grpc</projectUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<releaseNotes>Release 0.5.1 of gRPC C#</releaseNotes>
<releaseNotes>Release $version$ of gRPC C#</releaseNotes>
<copyright>Copyright 2015, Google Inc.</copyright>
<tags>gRPC RPC Protocol HTTP/2 Auth OAuth2</tags>
<dependencies>
<dependency id="BouncyCastle" version="1.7.0" />
<dependency id="Google.Apis.Auth" version="1.9.1" />
<dependency id="Grpc.Core" version="0.5.1" />
<dependency id="Grpc.Core" version="$version$" />
</dependencies>
</metadata>
<files>

@ -9,6 +9,5 @@ using System.Runtime.CompilerServices;
[assembly: AssemblyCopyright("Google Inc. All rights reserved.")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: AssemblyVersion("0.5.*")]
[assembly: InternalsVisibleTo("Grpc.Auth.Tests")]

@ -73,12 +73,6 @@ namespace Grpc.Core.Tests
Server server;
Channel channel;
[TestFixtureSetUp]
public void InitClass()
{
GrpcEnvironment.Initialize();
}
[SetUp]
public void Init()
{

@ -37,6 +37,9 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="..\Grpc.Core\Version.cs">
<Link>Version.cs</Link>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ClientServerTest.cs" />
<Compile Include="ServerTest.cs" />
@ -63,4 +66,4 @@
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<ItemGroup />
</Project>
</Project>

@ -43,16 +43,17 @@ namespace Grpc.Core.Tests
[Test]
public void InitializeAndShutdownGrpcEnvironment()
{
GrpcEnvironment.Initialize();
Assert.IsNotNull(GrpcEnvironment.ThreadPool.CompletionQueue);
var env = GrpcEnvironment.GetInstance();
Assert.IsNotNull(env.CompletionQueue);
GrpcEnvironment.Shutdown();
}
[Test]
public void SubsequentInvocations()
{
GrpcEnvironment.Initialize();
GrpcEnvironment.Initialize();
var env1 = GrpcEnvironment.GetInstance();
var env2 = GrpcEnvironment.GetInstance();
Assert.IsTrue(object.ReferenceEquals(env1, env2));
GrpcEnvironment.Shutdown();
GrpcEnvironment.Shutdown();
}
@ -60,15 +61,13 @@ namespace Grpc.Core.Tests
[Test]
public void InitializeAfterShutdown()
{
GrpcEnvironment.Initialize();
var tp1 = GrpcEnvironment.ThreadPool;
var env1 = GrpcEnvironment.GetInstance();
GrpcEnvironment.Shutdown();
GrpcEnvironment.Initialize();
var tp2 = GrpcEnvironment.ThreadPool;
var env2 = GrpcEnvironment.GetInstance();
GrpcEnvironment.Shutdown();
Assert.IsFalse(object.ReferenceEquals(tp1, tp2));
Assert.IsFalse(object.ReferenceEquals(env1, env2));
}
}
}

@ -53,18 +53,6 @@ namespace Grpc.Core.Tests
[DllImport("grpc_csharp_ext.dll")]
static extern IntPtr grpcsharp_test_nop(IntPtr ptr);
[TestFixtureSetUp]
public void Init()
{
GrpcEnvironment.Initialize();
}
[TestFixtureTearDown]
public void Cleanup()
{
GrpcEnvironment.Shutdown();
}
/// <summary>
/// (~1.26us .NET Windows)
/// </summary>

@ -9,4 +9,3 @@ using System.Runtime.CompilerServices;
[assembly: AssemblyCopyright("Google Inc. All rights reserved.")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: AssemblyVersion("0.5.*")]

@ -44,13 +44,10 @@ namespace Grpc.Core.Tests
[Test]
public void StartAndShutdownServer()
{
GrpcEnvironment.Initialize();
Server server = new Server();
server.AddListeningPort("localhost", Server.PickUnusedPort);
server.Start();
server.ShutdownAsync().Wait();
GrpcEnvironment.Shutdown();
}
}

@ -58,7 +58,7 @@ namespace Grpc.Core
where TResponse : class
{
var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer);
asyncCall.Initialize(call.Channel, GetCompletionQueue(), call.Name);
asyncCall.Initialize(call.Channel, call.Channel.CompletionQueue, call.Name);
var asyncResult = asyncCall.UnaryCallAsync(req, call.Headers);
RegisterCancellationCallback(asyncCall, token);
return await asyncResult;
@ -69,7 +69,7 @@ namespace Grpc.Core
where TResponse : class
{
var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer);
asyncCall.Initialize(call.Channel, GetCompletionQueue(), call.Name);
asyncCall.Initialize(call.Channel, call.Channel.CompletionQueue, call.Name);
asyncCall.StartServerStreamingCall(req, call.Headers);
RegisterCancellationCallback(asyncCall, token);
var responseStream = new ClientResponseStream<TRequest, TResponse>(asyncCall);
@ -81,7 +81,7 @@ namespace Grpc.Core
where TResponse : class
{
var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer);
asyncCall.Initialize(call.Channel, GetCompletionQueue(), call.Name);
asyncCall.Initialize(call.Channel, call.Channel.CompletionQueue, call.Name);
var resultTask = asyncCall.ClientStreamingCallAsync(call.Headers);
RegisterCancellationCallback(asyncCall, token);
var requestStream = new ClientRequestStream<TRequest, TResponse>(asyncCall);
@ -93,7 +93,7 @@ namespace Grpc.Core
where TResponse : class
{
var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer);
asyncCall.Initialize(call.Channel, GetCompletionQueue(), call.Name);
asyncCall.Initialize(call.Channel, call.Channel.CompletionQueue, call.Name);
asyncCall.StartDuplexStreamingCall(call.Headers);
RegisterCancellationCallback(asyncCall, token);
var requestStream = new ClientRequestStream<TRequest, TResponse>(asyncCall);
@ -108,13 +108,5 @@ namespace Grpc.Core
token.Register(() => asyncCall.Cancel());
}
}
/// <summary>
/// Gets shared completion queue used for async calls.
/// </summary>
private static CompletionQueueSafeHandle GetCompletionQueue()
{
return GrpcEnvironment.ThreadPool.CompletionQueue;
}
}
}

@ -42,8 +42,10 @@ namespace Grpc.Core
/// </summary>
public class Channel : IDisposable
{
readonly GrpcEnvironment environment;
readonly ChannelSafeHandle handle;
readonly string target;
bool disposed;
/// <summary>
/// Creates a channel that connects to a specific host.
@ -54,6 +56,7 @@ namespace Grpc.Core
/// <param name="options">Channel options.</param>
public Channel(string host, Credentials credentials = null, IEnumerable<ChannelOption> options = null)
{
this.environment = GrpcEnvironment.GetInstance();
using (ChannelArgsSafeHandle nativeChannelArgs = ChannelOptions.CreateChannelArgs(options))
{
if (credentials != null)
@ -105,10 +108,35 @@ namespace Grpc.Core
}
}
internal CompletionQueueSafeHandle CompletionQueue
{
get
{
return this.environment.CompletionQueue;
}
}
internal CompletionRegistry CompletionRegistry
{
get
{
return this.environment.CompletionRegistry;
}
}
internal GrpcEnvironment Environment
{
get
{
return this.environment;
}
}
protected virtual void Dispose(bool disposing)
{
if (handle != null && !handle.IsInvalid)
if (disposing && handle != null && !disposed)
{
disposed = true;
handle.Dispose();
}
}

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

Loading…
Cancel
Save