Merge branch 'master' of github.com:grpc/grpc into the-purge-2

Conflicts:
	src/cpp/client/channel.cc
	vsprojects/vs2010/grpc++.vcxproj
	vsprojects/vs2013/grpc++.vcxproj.filters
pull/1227/head
Nicolas "Pixel" Noble 10 years ago
commit 23be280366
  1. 3
      .travis.yml
  2. 27
      BUILD
  3. 1
      CONTRIBUTING.md
  4. 15362
      Makefile
  5. 237
      build.json
  6. 94
      doc/interop-test-descriptions.md
  7. 1
      examples/pubsub/README
  8. 79
      examples/pubsub/label.proto
  9. 10
      examples/pubsub/main.cc
  10. 729
      examples/pubsub/pubsub.proto
  11. 88
      gRPC.podspec
  12. 1
      include/grpc++/channel_interface.h
  13. 9
      include/grpc++/impl/internal_stub.h
  14. 10
      include/grpc++/impl/rpc_method.h
  15. 2
      include/grpc++/impl/rpc_service_method.h
  16. 31
      include/grpc/grpc.h
  17. 2
      include/grpc/support/atm_win32.h
  18. 44
      include/grpc/support/port_platform.h
  19. 8
      include/grpc/support/slice_buffer.h
  20. 2
      include/grpc/support/sync.h
  21. 53
      include/grpc/support/tls.h
  22. 52
      include/grpc/support/tls_gcc.h
  23. 52
      include/grpc/support/tls_msvc.h
  24. 53
      include/grpc/support/tls_pthread.h
  25. 149
      src/compiler/cpp_generator.cc
  26. 51
      src/core/channel/call_op_string.c
  27. 24
      src/core/channel/census_filter.c
  28. 57
      src/core/channel/channel_stack.c
  29. 31
      src/core/channel/channel_stack.h
  30. 99
      src/core/channel/client_channel.c
  31. 77
      src/core/channel/connected_channel.c
  32. 77
      src/core/channel/http_client_filter.c
  33. 240
      src/core/channel/http_server_filter.c
  34. 149
      src/core/channel/metadata_buffer.c
  35. 70
      src/core/channel/metadata_buffer.h
  36. 13
      src/core/httpcli/httpcli.c
  37. 42
      src/core/httpcli/httpcli_security_connector.c
  38. 12
      src/core/httpcli/httpcli_security_connector.h
  39. 85
      src/core/iomgr/endpoint_pair_windows.c
  40. 9
      src/core/iomgr/iocp_windows.c
  41. 4
      src/core/iomgr/resolve_address_windows.c
  42. 3
      src/core/iomgr/socket_windows.c
  43. 6
      src/core/iomgr/tcp_client_windows.c
  44. 11
      src/core/iomgr/tcp_posix.c
  45. 3
      src/core/iomgr/tcp_server_posix.c
  46. 12
      src/core/iomgr/tcp_server_windows.c
  47. 29
      src/core/iomgr/tcp_windows.c
  48. 138
      src/core/profiling/timers.c
  49. 71
      src/core/profiling/timers.h
  50. 36
      src/core/profiling/timers_preciseclock.h
  51. 91
      src/core/security/auth.c
  52. 236
      src/core/security/credentials.c
  53. 47
      src/core/security/credentials.h
  54. 4
      src/core/security/google_default_credentials.c
  55. 14
      src/core/security/secure_transport_setup.c
  56. 4
      src/core/security/secure_transport_setup.h
  57. 294
      src/core/security/security_connector.c
  58. 201
      src/core/security/security_connector.h
  59. 215
      src/core/security/security_context.h
  60. 28
      src/core/security/server_secure_chttp2.c
  61. 38
      src/core/support/file.c
  62. 6
      src/core/support/file.h
  63. 2
      src/core/support/histogram.c
  64. 57
      src/core/support/slice_buffer.c
  65. 20
      src/core/support/time_win32.c
  66. 225
      src/core/surface/call.c
  67. 25
      src/core/surface/call.h
  68. 123
      src/core/surface/channel.c
  69. 28
      src/core/surface/client.c
  70. 9
      src/core/surface/init.c
  71. 42
      src/core/surface/lame_client.c
  72. 44
      src/core/surface/secure_channel_create.c
  73. 40
      src/core/surface/server.c
  74. 1
      src/core/transport/chttp2/frame.h
  75. 8
      src/core/transport/chttp2/frame_settings.c
  76. 116
      src/core/transport/chttp2/stream_encoder.c
  77. 135
      src/core/transport/chttp2_transport.c
  78. 47
      src/core/transport/metadata.c
  79. 12
      src/core/transport/metadata.h
  80. 198
      src/core/transport/stream_op.c
  81. 54
      src/core/transport/stream_op.h
  82. 23
      src/cpp/client/channel.cc
  83. 1
      src/cpp/client/channel.h
  84. 12
      src/cpp/client/generic_stub.cc
  85. 5
      src/cpp/common/call.cc
  86. 4
      src/cpp/server/async_server_context.cc
  87. 5
      src/cpp/server/server.cc
  88. 1
      src/cpp/util/byte_buffer.cc
  89. 3
      src/csharp/Grpc.Core/.gitignore
  90. 25
      src/csharp/Grpc.Core/Grpc.Core.csproj
  91. 8
      src/csharp/Grpc.Core/Grpc.Core.nuspec
  92. 4
      src/csharp/Grpc.Core/packages.config
  93. 2
      src/csharp/Grpc.Examples.MathServer/MathServer.cs
  94. 5
      src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj
  95. 2
      src/csharp/Grpc.IntegrationTesting/packages.config
  96. 6
      src/node/README.md
  97. 40
      src/node/examples/math_server.js
  98. 44
      src/node/examples/pubsub/empty.proto
  99. 79
      src/node/examples/pubsub/label.proto
  100. 734
      src/node/examples/pubsub/pubsub.proto
  101. Some files were not shown because too many files have changed in this diff Show More

@ -4,12 +4,13 @@ before_install:
- sudo add-apt-repository ppa:h-rayflood/llvm -y
- sudo apt-get update -qq
- sudo apt-get install -qq libgtest-dev libgflags-dev python-virtualenv clang-3.5
- sudo pip install cpp-coveralls
- sudo pip install cpp-coveralls mako simplejson
env:
global:
- RUBY_VERSION=2.1
- COVERALLS_PARALLEL=true
matrix:
- CONFIG=opt TEST=sanity
- CONFIG=dbg TEST=c
- CONFIG=dbg TEST=c++
- CONFIG=opt TEST=c

27
BUILD

@ -1,5 +1,7 @@
# GRPC Bazel BUILD file.
# This currently builds C and C++ code.
# This file has been automatically generated from a template file.
# Please look at the templates directory instead.
# Copyright 2015, Google Inc.
# All rights reserved.
@ -104,6 +106,10 @@ cc_library(
"include/grpc/support/sync_win32.h",
"include/grpc/support/thd.h",
"include/grpc/support/time.h",
"include/grpc/support/tls.h",
"include/grpc/support/tls_gcc.h",
"include/grpc/support/tls_msvc.h",
"include/grpc/support/tls_pthread.h",
"include/grpc/support/useful.h",
],
includes = [
@ -120,7 +126,7 @@ cc_library(
srcs = [
"src/core/httpcli/format_request.h",
"src/core/httpcli/httpcli.h",
"src/core/httpcli/httpcli_security_context.h",
"src/core/httpcli/httpcli_security_connector.h",
"src/core/httpcli/parser.h",
"src/core/security/auth.h",
"src/core/security/base64.h",
@ -128,7 +134,7 @@ cc_library(
"src/core/security/json_token.h",
"src/core/security/secure_endpoint.h",
"src/core/security/secure_transport_setup.h",
"src/core/security/security_context.h",
"src/core/security/security_connector.h",
"src/core/tsi/fake_transport_security.h",
"src/core/tsi/ssl_transport_security.h",
"src/core/tsi/transport_security.h",
@ -143,7 +149,6 @@ cc_library(
"src/core/channel/http_client_filter.h",
"src/core/channel/http_filter.h",
"src/core/channel/http_server_filter.h",
"src/core/channel/metadata_buffer.h",
"src/core/channel/noop_filter.h",
"src/core/compression/algorithm.h",
"src/core/compression/message_compress.h",
@ -182,6 +187,8 @@ cc_library(
"src/core/json/json_common.h",
"src/core/json/json_reader.h",
"src/core/json/json_writer.h",
"src/core/profiling/timers.h",
"src/core/profiling/timers_preciseclock.h",
"src/core/statistics/census_interface.h",
"src/core/statistics/census_log.h",
"src/core/statistics/census_rpc_stats.h",
@ -222,19 +229,18 @@ cc_library(
"src/core/transport/transport_impl.h",
"src/core/httpcli/format_request.c",
"src/core/httpcli/httpcli.c",
"src/core/httpcli/httpcli_security_context.c",
"src/core/httpcli/httpcli_security_connector.c",
"src/core/httpcli/parser.c",
"src/core/security/auth.c",
"src/core/security/base64.c",
"src/core/security/credentials.c",
"src/core/security/credentials_posix.c",
"src/core/security/credentials_win32.c",
"src/core/security/factories.c",
"src/core/security/google_default_credentials.c",
"src/core/security/json_token.c",
"src/core/security/secure_endpoint.c",
"src/core/security/secure_transport_setup.c",
"src/core/security/security_context.c",
"src/core/security/security_connector.c",
"src/core/security/server_secure_chttp2.c",
"src/core/surface/init_secure.c",
"src/core/surface/secure_channel_create.c",
@ -252,7 +258,6 @@ cc_library(
"src/core/channel/http_client_filter.c",
"src/core/channel/http_filter.c",
"src/core/channel/http_server_filter.c",
"src/core/channel/metadata_buffer.c",
"src/core/channel/noop_filter.c",
"src/core/compression/algorithm.c",
"src/core/compression/message_compress.c",
@ -261,6 +266,7 @@ cc_library(
"src/core/iomgr/alarm_heap.c",
"src/core/iomgr/endpoint.c",
"src/core/iomgr/endpoint_pair_posix.c",
"src/core/iomgr/endpoint_pair_windows.c",
"src/core/iomgr/fd_posix.c",
"src/core/iomgr/iocp_windows.c",
"src/core/iomgr/iomgr.c",
@ -293,6 +299,7 @@ cc_library(
"src/core/json/json_reader.c",
"src/core/json/json_string.c",
"src/core/json/json_writer.c",
"src/core/profiling/timers.c",
"src/core/statistics/census_init.c",
"src/core/statistics/census_log.c",
"src/core/statistics/census_rpc_stats.c",
@ -370,7 +377,6 @@ cc_library(
"src/core/channel/http_client_filter.h",
"src/core/channel/http_filter.h",
"src/core/channel/http_server_filter.h",
"src/core/channel/metadata_buffer.h",
"src/core/channel/noop_filter.h",
"src/core/compression/algorithm.h",
"src/core/compression/message_compress.h",
@ -409,6 +415,8 @@ cc_library(
"src/core/json/json_common.h",
"src/core/json/json_reader.h",
"src/core/json/json_writer.h",
"src/core/profiling/timers.h",
"src/core/profiling/timers_preciseclock.h",
"src/core/statistics/census_interface.h",
"src/core/statistics/census_log.h",
"src/core/statistics/census_rpc_stats.h",
@ -459,7 +467,6 @@ cc_library(
"src/core/channel/http_client_filter.c",
"src/core/channel/http_filter.c",
"src/core/channel/http_server_filter.c",
"src/core/channel/metadata_buffer.c",
"src/core/channel/noop_filter.c",
"src/core/compression/algorithm.c",
"src/core/compression/message_compress.c",
@ -468,6 +475,7 @@ cc_library(
"src/core/iomgr/alarm_heap.c",
"src/core/iomgr/endpoint.c",
"src/core/iomgr/endpoint_pair_posix.c",
"src/core/iomgr/endpoint_pair_windows.c",
"src/core/iomgr/fd_posix.c",
"src/core/iomgr/iocp_windows.c",
"src/core/iomgr/iomgr.c",
@ -500,6 +508,7 @@ cc_library(
"src/core/json/json_reader.c",
"src/core/json/json_string.c",
"src/core/json/json_writer.c",
"src/core/profiling/timers.c",
"src/core/statistics/census_init.c",
"src/core/statistics/census_log.c",
"src/core/statistics/census_rpc_stats.c",

@ -51,3 +51,4 @@ re-generate the project files using the following command:
`./tools/buildgen/generate_projects.sh`
You'll find more information about this in the [templates](templates) folder.

15362
Makefile

File diff suppressed because one or more lines are too long

@ -1,4 +1,7 @@
{
"#1": "This file describes the list of targets and dependencies.",
"#2": "It is used among other things to generate all of our project files.",
"#3": "Please refer to the templates directory for more information.",
"settings": {
"#": "The public version number of the library.",
"version": {
@ -98,7 +101,6 @@
"src/core/channel/http_client_filter.h",
"src/core/channel/http_filter.h",
"src/core/channel/http_server_filter.h",
"src/core/channel/metadata_buffer.h",
"src/core/channel/noop_filter.h",
"src/core/compression/algorithm.h",
"src/core/compression/message_compress.h",
@ -137,6 +139,8 @@
"src/core/json/json_common.h",
"src/core/json/json_reader.h",
"src/core/json/json_writer.h",
"src/core/profiling/timers.h",
"src/core/profiling/timers_preciseclock.h",
"src/core/statistics/census_interface.h",
"src/core/statistics/census_log.h",
"src/core/statistics/census_rpc_stats.h",
@ -188,7 +192,6 @@
"src/core/channel/http_client_filter.c",
"src/core/channel/http_filter.c",
"src/core/channel/http_server_filter.c",
"src/core/channel/metadata_buffer.c",
"src/core/channel/noop_filter.c",
"src/core/compression/algorithm.c",
"src/core/compression/message_compress.c",
@ -197,6 +200,7 @@
"src/core/iomgr/alarm_heap.c",
"src/core/iomgr/endpoint.c",
"src/core/iomgr/endpoint_pair_posix.c",
"src/core/iomgr/endpoint_pair_windows.c",
"src/core/iomgr/fd_posix.c",
"src/core/iomgr/iocp_windows.c",
"src/core/iomgr/iomgr.c",
@ -229,6 +233,7 @@
"src/core/json/json_reader.c",
"src/core/json/json_string.c",
"src/core/json/json_writer.c",
"src/core/profiling/timers.c",
"src/core/statistics/census_init.c",
"src/core/statistics/census_log.c",
"src/core/statistics/census_rpc_stats.c",
@ -274,6 +279,19 @@
"src/core/transport/stream_op.c",
"src/core/transport/transport.c"
]
},
{
"name": "grpc_test_util_base",
"src": [
"test/core/end2end/cq_verifier.c",
"test/core/iomgr/endpoint_tests.c",
"test/core/statistics/census_log_tests.c",
"test/core/util/grpc_profiler.c",
"test/core/util/parse_hexstring.c",
"test/core/util/port_posix.c",
"test/core/util/port_windows.c",
"test/core/util/slice_splitter.c"
]
}
],
"libs": [
@ -303,6 +321,10 @@
"include/grpc/support/sync_win32.h",
"include/grpc/support/thd.h",
"include/grpc/support/time.h",
"include/grpc/support/tls.h",
"include/grpc/support/tls_gcc.h",
"include/grpc/support/tls_msvc.h",
"include/grpc/support/tls_pthread.h",
"include/grpc/support/useful.h"
],
"headers": [
@ -366,6 +388,7 @@
"deps": [
"gpr"
],
"secure": "no",
"vs_project_guid": "{EAB0A629-17A9-44DB-B5FF-E91A721FE037}"
},
{
@ -378,7 +401,7 @@
"headers": [
"src/core/httpcli/format_request.h",
"src/core/httpcli/httpcli.h",
"src/core/httpcli/httpcli_security_context.h",
"src/core/httpcli/httpcli_security_connector.h",
"src/core/httpcli/parser.h",
"src/core/security/auth.h",
"src/core/security/base64.h",
@ -386,7 +409,7 @@
"src/core/security/json_token.h",
"src/core/security/secure_endpoint.h",
"src/core/security/secure_transport_setup.h",
"src/core/security/security_context.h",
"src/core/security/security_connector.h",
"src/core/tsi/fake_transport_security.h",
"src/core/tsi/ssl_transport_security.h",
"src/core/tsi/transport_security.h",
@ -395,19 +418,18 @@
"src": [
"src/core/httpcli/format_request.c",
"src/core/httpcli/httpcli.c",
"src/core/httpcli/httpcli_security_context.c",
"src/core/httpcli/httpcli_security_connector.c",
"src/core/httpcli/parser.c",
"src/core/security/auth.c",
"src/core/security/base64.c",
"src/core/security/credentials.c",
"src/core/security/credentials_posix.c",
"src/core/security/credentials_win32.c",
"src/core/security/factories.c",
"src/core/security/google_default_credentials.c",
"src/core/security/json_token.c",
"src/core/security/secure_endpoint.c",
"src/core/security/secure_transport_setup.c",
"src/core/security/security_context.c",
"src/core/security/security_connector.c",
"src/core/security/server_secure_chttp2.c",
"src/core/surface/init_secure.c",
"src/core/surface/secure_channel_create.c",
@ -430,25 +452,35 @@
"build": "private",
"language": "c",
"src": [
"test/core/end2end/cq_verifier.c",
"test/core/end2end/data/server1_cert.c",
"test/core/end2end/data/server1_key.c",
"test/core/end2end/data/test_root_cert.c",
"test/core/iomgr/endpoint_tests.c",
"test/core/statistics/census_log_tests.c",
"test/core/transport/transport_end2end_tests.c",
"test/core/util/grpc_profiler.c",
"test/core/util/parse_hexstring.c",
"test/core/util/port_posix.c",
"test/core/util/slice_splitter.c"
"test/core/end2end/data/test_root_cert.c"
],
"deps": [
"gpr",
"gpr_test_util",
"grpc"
],
"filegroups": [
"grpc_test_util_base"
],
"vs_project_guid": "{17BCAFC0-5FDC-4C94-AEB9-95F3E220614B}"
},
{
"name": "grpc_test_util_unsecure",
"build": "private",
"language": "c",
"deps": [
"gpr",
"gpr_test_util",
"grpc"
],
"filegroups": [
"grpc_test_util_base"
],
"secure": "no",
"vs_project_guid": "{0A7E7F92-FDEA-40F1-A9EC-3BA484F98BBF}"
},
{
"name": "grpc_unsecure",
"build": "all",
@ -489,6 +521,14 @@
"secure": "check",
"vs_project_guid": "{C187A093-A0FE-489D-A40A-6E33DE0F9FEB}"
},
{
"name": "grpc++_test_config",
"build": "private",
"language": "c++",
"src": [
"test/cpp/util/test_config.cc"
]
},
{
"name": "grpc++_test_util",
"build": "private",
@ -542,15 +582,11 @@
"secure": "no"
},
{
"name": "interop_client_lib",
"name": "interop_client_helper",
"build": "private",
"language": "c++",
"src": [
"test/cpp/interop/empty.proto",
"test/cpp/interop/messages.proto",
"test/cpp/interop/test.proto",
"test/cpp/interop/client_helper.cc",
"test/cpp/interop/interop_client.cc"
"test/cpp/interop/client_helper.cc"
],
"deps": [
"grpc++_test_util",
@ -561,7 +597,28 @@
]
},
{
"name": "interop_server_lib",
"name": "interop_client_main",
"build": "private",
"language": "c++",
"src": [
"test/proto/empty.proto",
"test/proto/messages.proto",
"test/proto/test.proto",
"test/cpp/interop/client.cc",
"test/cpp/interop/interop_client.cc"
],
"deps": [
"grpc++_test_util",
"grpc_test_util",
"grpc++",
"grpc",
"gpr_test_util",
"gpr",
"grpc++_test_config"
]
},
{
"name": "interop_server_helper",
"build": "private",
"language": "c++",
"src": [
@ -575,9 +632,29 @@
]
},
{
"name": "pubsub_client_lib",
"name": "interop_server_main",
"build": "private",
"language": "c++",
"src": [
"test/proto/empty.proto",
"test/proto/messages.proto",
"test/proto/test.proto",
"test/cpp/interop/server.cc"
],
"deps": [
"grpc++_test_util",
"grpc_test_util",
"grpc++",
"grpc",
"gpr_test_util",
"gpr",
"grpc++_test_config"
]
},
{
"name": "pubsub_client_lib",
"build": "do_not_build",
"language": "c++",
"src": [
"examples/pubsub/label.proto",
"examples/pubsub/empty.proto",
@ -597,14 +674,24 @@
"language": "c++",
"headers": [
"test/cpp/qps/driver.h",
"test/cpp/qps/qps_worker.h",
"test/cpp/qps/report.h",
"test/cpp/qps/timer.h"
],
"src": [
"test/cpp/qps/qpstest.proto",
"test/cpp/qps/client_async.cc",
"test/cpp/qps/client_sync.cc",
"test/cpp/qps/driver.cc",
"test/cpp/qps/qps_worker.cc",
"test/cpp/qps/report.cc",
"test/cpp/qps/server_async.cc",
"test/cpp/qps/server_sync.cc",
"test/cpp/qps/timer.cc"
],
"deps": [
"grpc_test_util",
"grpc++_test_util"
]
},
{
@ -876,20 +963,6 @@
"gpr"
]
},
{
"name": "chttp2_transport_end2end_test",
"build": "test",
"language": "c",
"src": [
"test/core/transport/chttp2_transport_end2end_test.c"
],
"deps": [
"grpc_test_util",
"grpc",
"gpr_test_util",
"gpr"
]
},
{
"name": "dualstack_socket_test",
"build": "test",
@ -902,6 +975,9 @@
"grpc",
"gpr_test_util",
"gpr"
],
"platforms": [
"posix"
]
},
{
@ -1189,6 +1265,18 @@
"gpr"
]
},
{
"name": "gpr_tls_test",
"build": "test",
"language": "c",
"src": [
"test/core/support/tls_test.c"
],
"deps": [
"gpr_test_util",
"gpr"
]
},
{
"name": "gpr_useful_test",
"build": "test",
@ -1509,20 +1597,6 @@
"gpr"
]
},
{
"name": "metadata_buffer_test",
"build": "test",
"language": "c",
"src": [
"test/core/channel/metadata_buffer_test.c"
],
"deps": [
"grpc_test_util",
"grpc",
"gpr_test_util",
"gpr"
]
},
{
"name": "multi_init_test",
"build": "test",
@ -1703,6 +1777,20 @@
"gpr"
]
},
{
"name": "timers_test",
"build": "test",
"language": "c",
"src": [
"test/core/profiling/timers_test.c"
],
"deps": [
"grpc_test_util",
"grpc",
"gpr_test_util",
"gpr"
]
},
{
"name": "transport_metadata_test",
"build": "test",
@ -1850,7 +1938,8 @@
"grpc++",
"grpc",
"gpr_test_util",
"gpr"
"gpr",
"grpc++_test_config"
]
},
{
@ -1906,17 +1995,17 @@
"build": "test",
"run": false,
"language": "c++",
"src": [
"test/cpp/interop/client.cc"
],
"src": [],
"deps": [
"interop_client_lib",
"interop_client_main",
"interop_client_helper",
"grpc++_test_util",
"grpc_test_util",
"grpc++",
"grpc",
"gpr_test_util",
"gpr"
"gpr",
"grpc++_test_config"
]
},
{
@ -1924,20 +2013,17 @@
"build": "test",
"run": false,
"language": "c++",
"src": [
"test/cpp/interop/empty.proto",
"test/cpp/interop/messages.proto",
"test/cpp/interop/test.proto",
"test/cpp/interop/server.cc"
],
"src": [],
"deps": [
"interop_server_lib",
"interop_server_main",
"interop_server_helper",
"grpc++_test_util",
"grpc_test_util",
"grpc++",
"grpc",
"gpr_test_util",
"gpr"
"gpr",
"grpc++_test_config"
]
},
{
@ -1956,7 +2042,7 @@
},
{
"name": "pubsub_client",
"build": "test",
"build": "do_not_build",
"run": false,
"language": "c++",
"src": [
@ -1968,12 +2054,13 @@
"grpc++",
"grpc",
"gpr_test_util",
"gpr"
"gpr",
"grpc++_test_config"
]
},
{
"name": "pubsub_publisher_test",
"build": "test",
"build": "do_not_build",
"language": "c++",
"src": [
"examples/pubsub/publisher_test.cc"
@ -1990,7 +2077,7 @@
},
{
"name": "pubsub_subscriber_test",
"build": "test",
"build": "do_not_build",
"language": "c++",
"src": [
"examples/pubsub/subscriber_test.cc"
@ -2020,7 +2107,8 @@
"grpc++",
"grpc",
"gpr_test_util",
"gpr"
"gpr",
"grpc++_test_config"
]
},
{
@ -2051,10 +2139,6 @@
"test/cpp/qps/server.h"
],
"src": [
"test/cpp/qps/client_async.cc",
"test/cpp/qps/client_sync.cc",
"test/cpp/qps/server_async.cc",
"test/cpp/qps/server_sync.cc",
"test/cpp/qps/worker.cc"
],
"deps": [
@ -2064,7 +2148,8 @@
"grpc++",
"grpc",
"gpr_test_util",
"gpr"
"gpr",
"grpc++_test_config"
]
},
{

@ -2,7 +2,7 @@ Interoperability Test Case Descriptions
=======================================
Client and server use
[test.proto](https://github.com/grpc/grpc/blob/master/test/cpp/interop/test.proto)
[test.proto](https://github.com/grpc/grpc/blob/master/test/proto/test.proto)
and the [gRPC over HTTP/2 v2
protocol](https://github.com/grpc/grpc-common/blob/master/PROTOCOL-HTTP2.md).
@ -30,6 +30,14 @@ Clients should accept these arguments:
* Whether to replace platform root CAs with
[ca.pem](https://github.com/grpc/grpc/blob/master/src/core/tsi/test_creds/ca.pem)
as the CA root
* --default_service_account=ACCOUNT_EMAIL
* Email of the GCE default service account. Only applicable
for compute_engine_creds test.
* --oauth_scope=SCOPE
* OAuth scope. For example, "https://www.googleapis.com/auth/xapi.zoo"
* --service_account_key_file=PATH
* The path to the service account JSON key file generated from GCE developer
console.
Clients must support TLS with ALPN. Clients must not disable certificate
checking.
@ -259,25 +267,26 @@ Asserts:
### compute_engine_creds
Status: Not yet implementable
This test is only for cloud-to-prod path.
This test verifies unary calls succeed in sending messages while using Service
Credentials from GCE metadata server. The client instance needs to be created
with desired oauth scope.
The test uses `--default_service_account` with GCE service account email and
`--oauth_scope` with the OAuth scope to use. For testing against
grpc-test.sandbox.google.com, "https://www.googleapis.com/auth/xapi.zoo" should
be passed in as `--oauth_scope`.
Server features:
* [UnaryCall][]
* [Compressable Payload][]
* SimpeResponse.username
* SimpleResponse.oauth_scope
* [Echo Authenticated Username][]
* [Echo OAuth Scope][]
Procedure:
1. Client sets flags default_service_account with GCE service account name and
oauth_scope with the oauth scope to use.
2. Client configures channel to use GCECredentials
3. Client calls UnaryCall on the channel with:
1. Client configures channel to use GCECredentials
2. Client calls UnaryCall on the channel with:
```
{
@ -293,32 +302,34 @@ Procedure:
Asserts:
* call was successful
* received SimpleResponse.username equals FLAGS_default_service_account
* received SimpleResponse.oauth_scope is in FLAGS_oauth_scope
* received SimpleResponse.username equals the value of `--default_service_account` flag
* received SimpleResponse.oauth_scope is in `--oauth_scope`
* response payload body is 314159 bytes in size
* clients are free to assert that the response payload body contents are zero
and comparing the entire response message against a golden response
### service_account_creds
Status: Not yet implementable
This test is only for cloud-to-prod path.
This test verifies unary calls succeed in sending messages while using JWT
signing keys (redeemed for OAuth2 access tokens by the auth implementation)
The test uses `--service_account_key_file` with the path to a json key file
downloaded from https://console.developers.google.com, and `--oauth_scope`
to the oauth scope. For testing against grpc-test.sandbox.google.com,
"https://www.googleapis.com/auth/xapi.zoo" should be passed in
as `--oauth_scope`.
Server features:
* [UnaryCall][]
* [Compressable Payload][]
* SimpleResponse.username
* SimpleResponse.oauth_scope
* [Echo Authenticated Username][]
* [Echo OAuth Scope][]
Procedure:
1. Client sets flags service_account_key_file with the path to json key file,
oauth_scope to the oauth scope.
2. Client configures the channel to use ServiceAccountCredentials.
3. Client calls UnaryCall with:
1. Client configures the channel to use ServiceAccountCredentials.
2. Client calls UnaryCall with:
```
{
@ -335,31 +346,32 @@ Procedure:
Asserts:
* call was successful
* received SimpleResponse.username is in the json key file read from
FLAGS_service_account_key_file
* received SimpleResponse.oauth_scope is in FLAGS_oauth_scope
`--service_account_key_file`
* received SimpleResponse.oauth_scope is in `--oauth_scope`
* response payload body is 314159 bytes in size
* clients are free to assert that the response payload body contents are zero
and comparing the entire response message against a golden response
### jwt_token_creds
Status: Not yet implementable
This test is only for cloud-to-prod path.
This test verifies unary calls succeed in sending messages while using JWT
token (created by the project's key file)
Test caller should set flag `--service_account_key_file` with the
path to json key file downloaded from
https://console.developers.google.com.
Server features:
* [UnaryCall][]
* [Compressable Payload][]
* SimpleResponse.username
* SimpleResponse.oauth_scope
* [Echo Authenticated Username][]
* [Echo OAuth Scope][]
Procedure:
1. Client sets flags service_account_key_file with the path to json key file
2. Client configures the channel to use JWTTokenCredentials.
3. Client calls UnaryCall with:
1. Client configures the channel to use JWTTokenCredentials.
2. Client calls UnaryCall with:
```
{
@ -375,7 +387,7 @@ Procedure:
Asserts:
* call was successful
* received SimpleResponse.username is in the json key file read from
FLAGS_service_account_key_file
`--service_account_key_file`
* response payload body is 314159 bytes in size
* clients are free to assert that the response payload body contents are zero
and comparing the entire response message against a golden response
@ -394,7 +406,8 @@ Server features:
back to client in both header and trailer. (TODO: this is not defined)
Procedure:
1. While sending custom metadata (ascii + binary) in the header, client calls UnaryCall with:
1. While sending custom metadata (ascii + binary) in the header, client calls
UnaryCall with:
```
{
@ -619,11 +632,6 @@ payload body of size SimpleRequest.response_size bytes and type as appropriate
for the SimpleRequest.response_type. If the server does not support the
response_type, then it should fail the RPC with INVALID_ARGUMENT.
If the request sets fill_username, the server should return the client username
it sees in field SimpleResponse.username. If the request sets fill_oauth_scope,
the server should return the oauth scope of the rpc in the form of "xapi_zoo"
in field SimpleResponse.oauth_scope.
### StreamingInputCall
[StreamingInputCall]: #streaminginputcall
@ -672,14 +680,30 @@ Interaction with flow control is unspecified.
Status: Pending
#### Echo Authenticated Username
[Echo Authenticated Username]: #echo-authenticated-username
If a SimpleRequest has fill_username=true and that request was successfully
authenticated, then the SimpleResponse should have username filled with the
canonical form of the authenticated source. The canonical form is dependent on
the authentication method, but is likely to be a base 10 integer identifier or
an email address.
#### Echo OAuth scope
[Echo OAuth Scope]: #echo-oauth-scope
If a SimpleRequest has fill_oauth_scope=true and that request was successfully
authenticated via OAuth, then the SimpleResponse should have oauth_scope filled
with the scope of the method being invoked.
Although a general server-side feature, most test servers won't implement this
feature. The TLS server grpc-test.sandbox.google.com:443 supports this feature.
It requires at least the OAuth scope
`https://www.googleapis.com/auth/xapi.zoo` for authentication to succeed.
Discussion:
Ideally, this would be communicated via metadata and not in the
request/response, but we want to use this test in code paths that don't yet
fully communicate metadata.

@ -1,3 +1,4 @@
NOTE: This example does not build and is being updated.
Experimental example code, likely to change.
Users should not attempt to run this code till this warning is removed.

@ -1,79 +0,0 @@
// This file will be moved to a new location.
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Labels provide a way to associate user-defined metadata with various
// objects. Labels may be used to organize objects into non-hierarchical
// groups; think metadata tags attached to mp3s.
syntax = "proto2";
package tech.label;
// A key-value pair applied to a given object.
message Label {
// The key of a label is a syntactically valid URL (as per RFC 1738) with
// the "scheme" and initial slashes omitted and with the additional
// restrictions noted below. Each key should be globally unique. The
// "host" portion is called the "namespace" and is not necessarily
// resolvable to a network endpoint. Instead, the namespace indicates what
// system or entity defines the semantics of the label. Namespaces do not
// restrict the set of objects to which a label may be associated.
//
// Keys are defined by the following grammar:
//
// key = hostname "/" kpath
// kpath = ksegment *[ "/" ksegment ]
// ksegment = alphadigit | *[ alphadigit | "-" | "_" | "." ]
//
// where "hostname" and "alphadigit" are defined as in RFC 1738.
//
// Example key:
// spanner.google.com/universe
required string key = 1;
// The value of the label.
oneof value {
// A string value.
string str_value = 2;
// An integer value.
int64 num_value = 3;
}
}
// A collection of labels, such as the set of all labels attached to an
// object. Each label in the set must have a different key.
//
// Users should prefer to embed "repeated Label" directly when possible.
// This message should only be used in cases where that isn't possible (e.g.
// with oneof).
message Labels {
repeated Label label = 1;
}

@ -45,6 +45,7 @@
#include <grpc++/create_channel.h>
#include <grpc++/credentials.h>
#include <grpc++/status.h>
#include "test/cpp/util/test_config.h"
#include "examples/pubsub/publisher.h"
#include "examples/pubsub/subscriber.h"
@ -54,13 +55,6 @@ DEFINE_string(server_host, "pubsub-staging.googleapis.com",
"Server host to connect to");
DEFINE_string(project_id, "", "GCE project id such as stoked-keyword-656");
// In some distros, gflags is in the namespace google, and in some others,
// in gflags. This hack is enabling us to find both.
namespace google {}
namespace gflags {}
using namespace google;
using namespace gflags;
namespace {
const char kTopic[] = "testtopics";
@ -71,7 +65,7 @@ const char kMessageData[] = "Test Data";
int main(int argc, char** argv) {
grpc_init();
ParseCommandLineFlags(&argc, &argv, true);
grpc::testing::InitTest(&argc, &argv, true);
gpr_log(GPR_INFO, "Start PUBSUB client");
std::ostringstream ss;

@ -1,729 +0,0 @@
// This file will be moved to a new location.
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Specification of the Pubsub API.
syntax = "proto2";
import "examples/pubsub/empty.proto";
import "examples/pubsub/label.proto";
package tech.pubsub;
// -----------------------------------------------------------------------------
// Overview of the Pubsub API
// -----------------------------------------------------------------------------
// This file describes an API for a Pubsub system. This system provides a
// reliable many-to-many communication mechanism between independently written
// publishers and subscribers where the publisher publishes messages to "topics"
// and each subscriber creates a "subscription" and consumes messages from it.
//
// (a) The pubsub system maintains bindings between topics and subscriptions.
// (b) A publisher publishes messages into a topic.
// (c) The pubsub system delivers messages from topics into relevant
// subscriptions.
// (d) A subscriber receives pending messages from its subscription and
// acknowledges or nacks each one to the pubsub system.
// (e) The pubsub system removes acknowledged messages from that subscription.
// -----------------------------------------------------------------------------
// Data Model
// -----------------------------------------------------------------------------
// The data model consists of the following:
//
// * Topic: A topic is a resource to which messages are published by publishers.
// Topics are named, and the name of the topic is unique within the pubsub
// system.
//
// * Subscription: A subscription records the subscriber's interest in a topic.
// It can optionally include a query to select a subset of interesting
// messages. The pubsub system maintains a logical cursor tracking the
// matching messages which still need to be delivered and acked so that
// they can retried as needed. The set of messages that have not been
// acknowledged is called the subscription backlog.
//
// * Message: A message is a unit of data that flows in the system. It contains
// opaque data from the publisher along with its labels.
//
// * Message Labels (optional): A set of opaque key, value pairs assigned
// by the publisher which the subscriber can use for filtering out messages
// in the topic. For example, a label with key "foo.com/device_type" and
// value "mobile" may be added for messages that are only relevant for a
// mobile subscriber; a subscriber on a phone may decide to create a
// subscription only for messages that have this label.
// -----------------------------------------------------------------------------
// Publisher Flow
// -----------------------------------------------------------------------------
// A publisher publishes messages to the topic using the Publish request:
//
// PubsubMessage message;
// message.set_data("....");
// Label label;
// label.set_key("foo.com/key1");
// label.set_str_value("value1");
// message.add_label(label);
// PublishRequest request;
// request.set_topic("topicName");
// request.set_message(message);
// PublisherService.Publish(request);
// -----------------------------------------------------------------------------
// Subscriber Flow
// -----------------------------------------------------------------------------
// The subscriber part of the API is richer than the publisher part and has a
// number of concepts w.r.t. subscription creation and monitoring:
//
// (1) A subscriber creates a subscription using the CreateSubscription call.
// It may specify an optional "query" to indicate that it wants to receive
// only messages with a certain set of labels using the label query syntax.
// It may also specify an optional truncation policy to indicate when old
// messages from the subcription can be removed.
//
// (2) A subscriber receives messages in one of two ways: via push or pull.
//
// (a) To receive messages via push, the PushConfig field must be specified in
// the Subscription parameter when creating a subscription. The PushConfig
// specifies an endpoint at which the subscriber must expose the
// PushEndpointService. Messages are received via the HandlePubsubEvent
// method. The push subscriber responds to the HandlePubsubEvent method
// with a result code that indicates one of three things: Ack (the message
// has been successfully processed and the Pubsub system may delete it),
// Nack (the message has been rejected, the Pubsub system should resend it
// at a later time), or Push-Back (this is a Nack with the additional
// semantics that the subscriber is overloaded and the pubsub system should
// back off on the rate at which it is invoking HandlePubsubEvent). The
// endpoint may be a load balancer for better scalability.
//
// (b) To receive messages via pull a subscriber calls the Pull method on the
// SubscriberService to get messages from the subscription. For each
// individual message, the subscriber may use the ack_id received in the
// PullResponse to Ack the message, Nack the message, or modify the ack
// deadline with ModifyAckDeadline. See the
// Subscription.ack_deadline_seconds field documentation for details on the
// ack deadline behavior.
//
// Note: Messages may be consumed in parallel by multiple subscribers making
// Pull calls to the same subscription; this will result in the set of
// messages from the subscription being shared and each subscriber
// receiving a subset of the messages.
//
// (4) The subscriber can explicitly truncate the current subscription.
//
// (5) "Truncated" events are delivered when a subscription is
// truncated, whether due to the subscription's truncation policy
// or an explicit request from the subscriber.
//
// Subscription creation:
//
// Subscription subscription;
// subscription.set_topic("topicName");
// subscription.set_name("subscriptionName");
// subscription.push_config().set_push_endpoint("machinename:8888");
// SubscriberService.CreateSubscription(subscription);
//
// Consuming messages via push:
//
// The port 'machinename:8888' must be bound to a server that implements
// the PushEndpointService with the following method:
//
// int HandlePubsubEvent(PubsubEvent event) {
// if (event.subscription().equals("subscriptionName")) {
// if (event.has_message()) {
// Process(event.message().data());
// } else if (event.truncated()) {
// ProcessTruncatedEvent();
// }
// }
// return OK; // This return code implies an acknowledgment
// }
//
// Consuming messages via pull:
//
// The subscription must be created without setting the push_config field.
//
// PullRequest pull_request;
// pull_request.set_subscription("subscriptionName");
// pull_request.set_return_immediately(false);
// while (true) {
// PullResponse pull_response;
// if (SubscriberService.Pull(pull_request, pull_response) == OK) {
// PubsubEvent event = pull_response.pubsub_event();
// if (event.has_message()) {
// Process(event.message().data());
// } else if (event.truncated()) {
// ProcessTruncatedEvent();
// }
// AcknowledgeRequest ack_request;
// ackRequest.set_subscription("subscriptionName");
// ackRequest.set_ack_id(pull_response.ack_id());
// SubscriberService.Acknowledge(ack_request);
// }
// }
// -----------------------------------------------------------------------------
// Reliability Semantics
// -----------------------------------------------------------------------------
// When a subscriber successfully creates a subscription using
// Subscriber.CreateSubscription, it establishes a "subscription point" with
// respect to that subscription - the subscriber is guaranteed to receive any
// message published after this subscription point that matches the
// subscription's query. Note that messages published before the Subscription
// point may or may not be delivered.
//
// If the system truncates the subscription according to the specified
// truncation policy, the system delivers a subscription status event with the
// "truncated" field set to true. We refer to such events as "truncation
// events". A truncation event:
//
// * Informs the subscriber that part of the subscription messages have been
// discarded. The subscriber may want to recover from the message loss, e.g.,
// by resyncing its state with its backend.
// * Establishes a new subscription point, i.e., the subscriber is guaranteed to
// receive all changes published after the trunction event is received (or
// until another truncation event is received).
//
// Note that messages are not delivered in any particular order by the pubsub
// system. Furthermore, the system guarantees at-least-once delivery
// of each message or truncation events until acked.
// -----------------------------------------------------------------------------
// Deletion
// -----------------------------------------------------------------------------
// Both topics and subscriptions may be deleted. Deletion of a topic implies
// deletion of all attached subscriptions.
//
// When a subscription is deleted directly by calling DeleteSubscription, all
// messages are immediately dropped. If it is a pull subscriber, future pull
// requests will return NOT_FOUND.
//
// When a topic is deleted all corresponding subscriptions are immediately
// deleted, and subscribers experience the same behavior as directly deleting
// the subscription.
// -----------------------------------------------------------------------------
// The Publisher service and its protos.
// -----------------------------------------------------------------------------
// The service that an application uses to manipulate topics, and to send
// messages to a topic.
service PublisherService {
// Creates the given topic with the given name.
rpc CreateTopic(Topic) returns (Topic) {
}
// Adds a message to the topic. Returns NOT_FOUND if the topic does not
// exist.
rpc Publish(PublishRequest) returns (proto2.Empty) {
}
// Adds one or more messages to the topic. Returns NOT_FOUND if the topic does
// not exist.
rpc PublishBatch(PublishBatchRequest) returns (PublishBatchResponse) {
}
// Gets the configuration of a topic. Since the topic only has the name
// attribute, this method is only useful to check the existence of a topic.
// If other attributes are added in the future, they will be returned here.
rpc GetTopic(GetTopicRequest) returns (Topic) {
}
// Lists matching topics.
rpc ListTopics(ListTopicsRequest) returns (ListTopicsResponse) {
}
// Deletes the topic with the given name. All subscriptions to this topic
// are also deleted. Returns NOT_FOUND if the topic does not exist.
// After a topic is deleted, a new topic may be created with the same name.
rpc DeleteTopic(DeleteTopicRequest) returns (proto2.Empty) {
}
}
// A topic resource.
message Topic {
// Name of the topic.
optional string name = 1;
}
// A message data and its labels.
message PubsubMessage {
// The message payload.
optional bytes data = 1;
// Optional list of labels for this message. Keys in this collection must
// be unique.
repeated tech.label.Label label = 2;
// ID of this message assigned by the server at publication time. Guaranteed
// to be unique within the topic. This value may be read by a subscriber
// that receives a PubsubMessage via a Pull call or a push delivery. It must
// not be populated by a publisher in a Publish call.
optional string message_id = 3;
}
// Request for the GetTopic method.
message GetTopicRequest {
// The name of the topic to get.
optional string topic = 1;
}
// Request for the Publish method.
message PublishRequest {
// The message in the request will be published on this topic.
optional string topic = 1;
// The message to publish.
optional PubsubMessage message = 2;
}
// Request for the PublishBatch method.
message PublishBatchRequest {
// The messages in the request will be published on this topic.
optional string topic = 1;
// The messages to publish.
repeated PubsubMessage messages = 2;
}
// Response for the PublishBatch method.
message PublishBatchResponse {
// The server-assigned ID of each published message, in the same order as
// the messages in the request. IDs are guaranteed to be unique within
// the topic.
repeated string message_ids = 1;
}
// Request for the ListTopics method.
message ListTopicsRequest {
// A valid label query expression.
// (-- Which labels are required or supported is implementation-specific. --)
optional string query = 1;
// Maximum number of topics to return.
// (-- If not specified or <= 0, the implementation will select a reasonable
// value. --)
optional int32 max_results = 2;
// The value obtained in the last <code>ListTopicsResponse</code>
// for continuation.
optional string page_token = 3;
}
// Response for the ListTopics method.
message ListTopicsResponse {
// The resulting topics.
repeated Topic topic = 1;
// If not empty, indicates that there are more topics that match the request,
// and this value should be passed to the next <code>ListTopicsRequest</code>
// to continue.
optional string next_page_token = 2;
}
// Request for the Delete method.
message DeleteTopicRequest {
// Name of the topic to delete.
optional string topic = 1;
}
// -----------------------------------------------------------------------------
// The Subscriber service and its protos.
// -----------------------------------------------------------------------------
// The service that an application uses to manipulate subscriptions and to
// consume messages from a subscription via the pull method.
service SubscriberService {
// Creates a subscription on a given topic for a given subscriber.
// If the subscription already exists, returns ALREADY_EXISTS.
// If the corresponding topic doesn't exist, returns NOT_FOUND.
//
// If the name is not provided in the request, the server will assign a random
// name for this subscription on the same project as the topic.
rpc CreateSubscription(Subscription) returns (Subscription) {
}
// Gets the configuration details of a subscription.
rpc GetSubscription(GetSubscriptionRequest) returns (Subscription) {
}
// Lists matching subscriptions.
rpc ListSubscriptions(ListSubscriptionsRequest)
returns (ListSubscriptionsResponse) {
}
// Deletes an existing subscription. All pending messages in the subscription
// are immediately dropped. Calls to Pull after deletion will return
// NOT_FOUND.
rpc DeleteSubscription(DeleteSubscriptionRequest) returns (proto2.Empty) {
}
// Removes all the pending messages in the subscription and releases the
// storage associated with them. Results in a truncation event to be sent to
// the subscriber. Messages added after this call returns are stored in the
// subscription as before.
rpc TruncateSubscription(TruncateSubscriptionRequest) returns (proto2.Empty) {
}
//
// Push subscriber calls.
//
// Modifies the <code>PushConfig</code> for a specified subscription.
// This method can be used to suspend the flow of messages to an endpoint
// by clearing the <code>PushConfig</code> field in the request. Messages
// will be accumulated for delivery even if no push configuration is
// defined or while the configuration is modified.
rpc ModifyPushConfig(ModifyPushConfigRequest) returns (proto2.Empty) {
}
//
// Pull Subscriber calls
//
// Pulls a single message from the server.
// If return_immediately is true, and no messages are available in the
// subscription, this method returns FAILED_PRECONDITION. The system is free
// to return an UNAVAILABLE error if no messages are available in a
// reasonable amount of time (to reduce system load).
rpc Pull(PullRequest) returns (PullResponse) {
}
// Pulls messages from the server. Returns an empty list if there are no
// messages available in the backlog. The system is free to return UNAVAILABLE
// if there are too many pull requests outstanding for the given subscription.
rpc PullBatch(PullBatchRequest) returns (PullBatchResponse) {
}
// Modifies the Ack deadline for a message received from a pull request.
rpc ModifyAckDeadline(ModifyAckDeadlineRequest) returns (proto2.Empty) {
}
// Acknowledges a particular received message: the Pub/Sub system can remove
// the given message from the subscription. Acknowledging a message whose
// Ack deadline has expired may succeed, but the message could have been
// already redelivered. Acknowledging a message more than once will not
// result in an error. This is only used for messages received via pull.
rpc Acknowledge(AcknowledgeRequest) returns (proto2.Empty) {
}
// Refuses processing a particular received message. The system will
// redeliver this message to some consumer of the subscription at some
// future time. This is only used for messages received via pull.
rpc Nack(NackRequest) returns (proto2.Empty) {
}
}
// A subscription resource.
message Subscription {
// Name of the subscription.
optional string name = 1;
// The name of the topic from which this subscription is receiving messages.
optional string topic = 2;
// If <code>query</code> is non-empty, only messages on the subscriber's
// topic whose labels match the query will be returned. Otherwise all
// messages on the topic will be returned.
// (-- The query syntax is described in label_query.proto --)
optional string query = 3;
// The subscriber may specify requirements for truncating unacknowledged
// subscription entries. The system will honor the
// <code>CreateSubscription</code> request only if it can meet these
// requirements. If this field is not specified, messages are never truncated
// by the system.
optional TruncationPolicy truncation_policy = 4;
// Specifies which messages can be truncated by the system.
message TruncationPolicy {
oneof policy {
// If <code>max_bytes</code> is specified, the system is allowed to drop
// old messages to keep the combined size of stored messages under
// <code>max_bytes</code>. This is a hint; the system may keep more than
// this many bytes, but will make a best effort to keep the size from
// growing much beyond this parameter.
int64 max_bytes = 1;
// If <code>max_age_seconds</code> is specified, the system is allowed to
// drop messages that have been stored for at least this many seconds.
// This is a hint; the system may keep these messages, but will make a
// best effort to remove them when their maximum age is reached.
int64 max_age_seconds = 2;
}
}
// If push delivery is used with this subscription, this field is
// used to configure it.
optional PushConfig push_config = 5;
// For either push or pull delivery, the value is the maximum time after a
// subscriber receives a message before the subscriber should acknowledge or
// Nack the message. If the Ack deadline for a message passes without an
// Ack or a Nack, the Pub/Sub system will eventually redeliver the message.
// If a subscriber acknowledges after the deadline, the Pub/Sub system may
// accept the Ack, but it is possible that the message has been already
// delivered again. Multiple Acks to the message are allowed and will
// succeed.
//
// For push delivery, this value is used to set the request timeout for
// the call to the push endpoint.
//
// For pull delivery, this value is used as the initial value for the Ack
// deadline. It may be overridden for a specific pull request (message) with
// <code>ModifyAckDeadline</code>.
// While a message is outstanding (i.e. it has been delivered to a pull
// subscriber and the subscriber has not yet Acked or Nacked), the Pub/Sub
// system will not deliver that message to another pull subscriber
// (on a best-effort basis).
optional int32 ack_deadline_seconds = 6;
// If this parameter is set to n, the system is allowed to (but not required
// to) delete the subscription when at least n seconds have elapsed since the
// client presence was detected. (Presence is detected through any
// interaction using the subscription ID, including Pull(), Get(), or
// acknowledging a message.)
//
// If this parameter is not set, the subscription will stay live until
// explicitly deleted.
//
// Clients can detect such garbage collection when a Get call or a Pull call
// (for pull subscribers only) returns NOT_FOUND.
optional int64 garbage_collect_seconds = 7;
}
// Configuration for a push delivery endpoint.
message PushConfig {
// A URL locating the endpoint to which messages should be pushed.
// For example, a Webhook endpoint might use "https://example.com/push".
// (-- An Android application might use "gcm:<REGID>", where <REGID> is a
// GCM registration id allocated for pushing messages to the application. --)
optional string push_endpoint = 1;
}
// An event indicating a received message or truncation event.
message PubsubEvent {
// The subscription that received the event.
optional string subscription = 1;
oneof type {
// A received message.
PubsubMessage message = 2;
// Indicates that this subscription has been truncated.
bool truncated = 3;
// Indicates that this subscription has been deleted. (Note that pull
// subscribers will always receive NOT_FOUND in response in their pull
// request on the subscription, rather than seeing this boolean.)
bool deleted = 4;
}
}
// Request for the GetSubscription method.
message GetSubscriptionRequest {
// The name of the subscription to get.
optional string subscription = 1;
}
// Request for the ListSubscriptions method.
message ListSubscriptionsRequest {
// A valid label query expression.
// (-- Which labels are required or supported is implementation-specific.
// TODO(eschapira): This method must support to query by topic. We must
// define the key URI for the "topic" label. --)
optional string query = 1;
// Maximum number of subscriptions to return.
// (-- If not specified or <= 0, the implementation will select a reasonable
// value. --)
optional int32 max_results = 3;
// The value obtained in the last <code>ListSubscriptionsResponse</code>
// for continuation.
optional string page_token = 4;
}
// Response for the ListSubscriptions method.
message ListSubscriptionsResponse {
// The subscriptions that match the request.
repeated Subscription subscription = 1;
// If not empty, indicates that there are more subscriptions that match the
// request and this value should be passed to the next
// <code>ListSubscriptionsRequest</code> to continue.
optional string next_page_token = 2;
}
// Request for the TruncateSubscription method.
message TruncateSubscriptionRequest {
// The subscription that is being truncated.
optional string subscription = 1;
}
// Request for the DeleteSubscription method.
message DeleteSubscriptionRequest {
// The subscription to delete.
optional string subscription = 1;
}
// Request for the ModifyPushConfig method.
message ModifyPushConfigRequest {
// The name of the subscription.
optional string subscription = 1;
// An empty <code>push_config</code> indicates that the Pub/Sub system should
// pause pushing messages from the given subscription.
optional PushConfig push_config = 2;
}
// -----------------------------------------------------------------------------
// The protos used by a pull subscriber.
// -----------------------------------------------------------------------------
// Request for the Pull method.
message PullRequest {
// The subscription from which a message should be pulled.
optional string subscription = 1;
// If this is specified as true the system will respond immediately even if
// it is not able to return a message in the Pull response. Otherwise the
// system is allowed to wait until at least one message is available rather
// than returning FAILED_PRECONDITION. The client may cancel the request if
// it does not wish to wait any longer for the response.
optional bool return_immediately = 2;
}
// Either a <code>PubsubMessage</code> or a truncation event. One of these two
// must be populated.
message PullResponse {
// This ID must be used to acknowledge the received event or message.
optional string ack_id = 1;
// A pubsub message or truncation event.
optional PubsubEvent pubsub_event = 2;
}
// Request for the PullBatch method.
message PullBatchRequest {
// The subscription from which messages should be pulled.
optional string subscription = 1;
// If this is specified as true the system will respond immediately even if
// it is not able to return a message in the Pull response. Otherwise the
// system is allowed to wait until at least one message is available rather
// than returning no messages. The client may cancel the request if it does
// not wish to wait any longer for the response.
optional bool return_immediately = 2;
// The maximum number of PubsubEvents returned for this request. The Pub/Sub
// system may return fewer than the number of events specified.
optional int32 max_events = 3;
}
// Response for the PullBatch method.
message PullBatchResponse {
// Received Pub/Sub messages or status events. The Pub/Sub system will return
// zero messages if there are no more messages available in the backlog. The
// Pub/Sub system may return fewer than the max_events requested even if
// there are more messages available in the backlog.
repeated PullResponse pull_responses = 2;
}
// Request for the ModifyAckDeadline method.
message ModifyAckDeadlineRequest {
// The name of the subscription from which messages are being pulled.
optional string subscription = 1;
// The acknowledgment ID.
optional string ack_id = 2;
// The new Ack deadline. Must be >= 0.
optional int32 ack_deadline_seconds = 3;
}
// Request for the Acknowledge method.
message AcknowledgeRequest {
// The subscription whose message is being acknowledged.
optional string subscription = 1;
// The acknowledgment ID for the message being acknowledged. This was
// returned by the Pub/Sub system in the Pull response.
repeated string ack_id = 2;
}
// Request for the Nack method.
message NackRequest {
// The subscription whose message is being Nacked.
optional string subscription = 1;
// The acknowledgment ID for the message being refused. This was returned by
// the Pub/Sub system in the Pull response.
repeated string ack_id = 2;
}
// -----------------------------------------------------------------------------
// The service and protos used by a push subscriber.
// -----------------------------------------------------------------------------
// The service that a subscriber uses to handle messages sent via push
// delivery.
// This service is not currently exported for HTTP clients.
// TODO(eschapira): Explain HTTP subscribers.
service PushEndpointService {
// Sends a <code>PubsubMessage</code> or a subscription status event to a
// push endpoint.
// The push endpoint responds with an empty message and a code from
// util/task/codes.proto. The following codes have a particular meaning to the
// Pub/Sub system:
// OK - This is interpreted by Pub/Sub as Ack.
// ABORTED - This is intepreted by Pub/Sub as a Nack, without implying
// pushback for congestion control. The Pub/Sub system will
// retry this message at a later time.
// UNAVAILABLE - This is intepreted by Pub/Sub as a Nack, with the additional
// semantics of push-back. The Pub/Sub system will use an AIMD
// congestion control algorithm to backoff the rate of sending
// messages from this subscription.
// Any other code, or a failure to respond, will be interpreted in the same
// way as ABORTED; i.e. the system will retry the message at a later time to
// ensure reliable delivery.
rpc HandlePubsubEvent(PubsubEvent) returns (proto2.Empty);
}

@ -0,0 +1,88 @@
Pod::Spec.new do |s|
s.name = 'gRPC'
s.version = '0.0.1'
s.summary = 'Generic gRPC client library for iOS'
s.homepage = 'https://www.grpc.io'
s.license = 'New BSD'
s.authors = { 'Jorge Canizales' => 'jcanizales@google.com' }
# s.source = { :git => 'https://github.com/grpc/grpc.git', :tag => 'release-0_5_0' }
s.platform = :ios
s.ios.deployment_target = '6.0'
s.requires_arc = true
s.subspec 'RxLibrary' do |rs|
rs.summary = 'Reactive Extensions library for iOS.'
rs.authors = { 'Jorge Canizales' => 'jcanizales@google.com' }
rs.source_files = 'src/objective-c/RxLibrary/*.{h,m}', 'src/objective-c/RxLibrary/transformations/*.{h,m}', 'src/objective-c/RxLibrary/private/*.{h,m}'
rs.private_header_files = 'src/objective-c/RxLibrary/private/*.h'
end
s.subspec 'C-Core' do |cs|
cs.summary = 'Core cross-platform gRPC library, written in C.'
cs.authors = { 'Craig Tiller' => 'ctiller@google.com',
'David Klempner' => 'klempner@google.com',
'Nicolas Noble' => 'nnoble@google.com',
'Vijay Pai' => 'vpai@google.com',
'Yang Gao' => 'yangg@google.com' }
cs.source_files = 'src/core/**/*.{h,c}', 'include/grpc/*.h', 'include/grpc/**/*.h'
cs.private_header_files = 'src/core/**/*.h'
cs.header_mappings_dir = '.'
cs.xcconfig = { 'HEADER_SEARCH_PATHS' => '"$(PODS_ROOT)/Headers/Build/gRPC" "$(PODS_ROOT)/Headers/Build/gRPC/include"' }
cs.requires_arc = false
cs.libraries = 'z'
cs.dependency 'OpenSSL', '~> 1.0.200'
# This is a workaround for Cocoapods Issue #1437.
# It renames time.h and string.h to grpc_time.h and grpc_string.h.
cs.prepare_command = <<-CMD
DIR_TIME="grpc/support"
BAD_TIME="$DIR_TIME/time.h"
GOOD_TIME="$DIR_TIME/grpc_time.h"
if [ -f "include/$BAD_TIME" ];
then
grep -rl "$BAD_TIME" include/grpc src/core | xargs sed -i '' -e s@$BAD_TIME@$GOOD_TIME@g
mv "include/$BAD_TIME" "include/$GOOD_TIME"
fi
DIR_STRING="src/core/support"
BAD_STRING="$DIR_STRING/string.h"
GOOD_STRING="$DIR_STRING/grpc_string.h"
if [ -f "$BAD_STRING" ];
then
grep -rl "$BAD_STRING" include/grpc src/core | xargs sed -i '' -e s@$BAD_STRING@$GOOD_STRING@g
mv "$BAD_STRING" "$GOOD_STRING"
fi
CMD
end
s.subspec 'GRPCClient' do |gs|
gs.summary = 'Objective-C wrapper around the core gRPC library.'
gs.authors = { 'Jorge Canizales' => 'jcanizales@google.com' }
gs.source_files = 'src/objective-c/GRPCClient/*.{h,m}', 'src/objective-c/GRPCClient/private/*.{h,m}'
gs.private_header_files = 'src/objective-c/GRPCClient/private/*.h'
gs.dependency 'gRPC/C-Core'
# Is this needed in all dependents?
gs.xcconfig = { 'HEADER_SEARCH_PATHS' => '"$(PODS_ROOT)/Headers/Public/gRPC/include"' }
gs.dependency 'gRPC/RxLibrary'
# Certificates, to be able to establish TLS connections:
gs.resource_bundles = { 'gRPC' => ['etc/roots.pem'] }
end
s.subspec 'ProtoRPC' do |ps|
ps.summary = 'RPC library for ProtocolBuffers, based on gRPC'
ps.authors = { 'Jorge Canizales' => 'jcanizales@google.com' }
ps.source_files = 'src/objective-c/ProtoRPC/*.{h,m}'
ps.dependency 'gRPC/GRPCClient'
ps.dependency 'gRPC/RxLibrary'
end
end

@ -51,6 +51,7 @@ class ChannelInterface : public CallHook {
public:
virtual ~ChannelInterface() {}
virtual void *RegisterMethod(const char *method_name) = 0;
virtual Call CreateCall(const RpcMethod& method, ClientContext* context,
CompletionQueue* cq) = 0;
};

@ -42,17 +42,14 @@ namespace grpc {
class InternalStub {
public:
InternalStub() {}
InternalStub(const std::shared_ptr<ChannelInterface>& channel)
: channel_(channel) {}
virtual ~InternalStub() {}
void set_channel(const std::shared_ptr<ChannelInterface>& channel) {
channel_ = channel;
}
ChannelInterface* channel() { return channel_.get(); }
private:
std::shared_ptr<ChannelInterface> channel_;
const std::shared_ptr<ChannelInterface> channel_;
};
} // namespace grpc

@ -45,17 +45,17 @@ class RpcMethod {
BIDI_STREAMING
};
explicit RpcMethod(const char* name)
: name_(name), method_type_(NORMAL_RPC) {}
RpcMethod(const char* name, RpcType type) : name_(name), method_type_(type) {}
RpcMethod(const char* name, RpcType type, void* channel_tag)
: name_(name), method_type_(type), channel_tag_(channel_tag) {}
const char* name() const { return name_; }
RpcType method_type() const { return method_type_; }
void* channel_tag() const { return channel_tag_; }
private:
const char* name_;
const char* const name_;
const RpcType method_type_;
void* const channel_tag_;
};
} // namespace grpc

@ -167,7 +167,7 @@ class RpcServiceMethod : public RpcMethod {
MethodHandler* handler,
grpc::protobuf::Message* request_prototype,
grpc::protobuf::Message* response_prototype)
: RpcMethod(name, type),
: RpcMethod(name, type, nullptr),
handler_(handler),
request_prototype_(request_prototype),
response_prototype_(response_prototype) {}

@ -186,6 +186,13 @@ typedef struct grpc_metadata {
const char *key;
const char *value;
size_t value_length;
/* The following fields are reserved for grpc internal use.
There is no need to initialize them, and they will be set to garbage during
calls to grpc. */
struct {
void *obfuscated[3];
} internal_data;
} grpc_metadata;
typedef enum grpc_completion_type {
@ -295,7 +302,7 @@ typedef struct grpc_op {
union {
struct {
size_t count;
const grpc_metadata *metadata;
grpc_metadata *metadata;
} send_initial_metadata;
grpc_byte_buffer *send_message;
struct {
@ -361,7 +368,7 @@ typedef struct grpc_op {
library). */
void grpc_init(void);
/* Shut down the grpc library.
/* Shut down the grpc library.
No memory is used by grpc after this call returns, nor are any instructions
executing within the grpc library.
Prior to calling, all application owned grpc objects must have been
@ -395,9 +402,9 @@ void grpc_event_finish(grpc_event *event);
/* Begin destruction of a completion queue. Once all possible events are
drained then grpc_completion_queue_next will start to produce
GRPC_QUEUE_SHUTDOWN events only. At that point it's safe to call
grpc_completion_queue_destroy.
GRPC_QUEUE_SHUTDOWN events only. At that point it's safe to call
grpc_completion_queue_destroy.
After calling this function applications should ensure that no
NEW work is added to be published on this completion queue. */
void grpc_completion_queue_shutdown(grpc_completion_queue *cq);
@ -421,6 +428,15 @@ grpc_call *grpc_channel_create_call(grpc_channel *channel,
const char *method, const char *host,
gpr_timespec deadline);
/* Pre-register a method/host pair on a channel. */
void *grpc_channel_register_call(grpc_channel *channel, const char *method,
const char *host);
/* Create a call given a handle returned from grpc_channel_register_call */
grpc_call *grpc_channel_create_registered_call(
grpc_channel *channel, grpc_completion_queue *completion_queue,
void *registered_call_handle, gpr_timespec deadline);
/* Start a batch of operations defined in the array ops; when complete, post a
completion of type 'tag' to the completion queue bound to the call.
The order of ops specified in the batch has no significance.
@ -579,8 +595,7 @@ grpc_call_error grpc_server_request_call_old(grpc_server *server,
grpc_call_error grpc_server_request_call(
grpc_server *server, grpc_call **call, grpc_call_details *details,
grpc_metadata_array *request_metadata,
grpc_completion_queue *cq_bound_to_call,
void *tag_new);
grpc_completion_queue *cq_bound_to_call, void *tag_new);
/* Registers a method in the server.
Methods to this (host, method) pair will not be reported by
@ -635,4 +650,4 @@ void grpc_server_destroy(grpc_server *server);
}
#endif
#endif /* GRPC_GRPC_H */
#endif /* GRPC_GRPC_H */

@ -73,7 +73,7 @@ static __inline int gpr_atm_no_barrier_cas(gpr_atm *p, gpr_atm o, gpr_atm n) {
static __inline int gpr_atm_acq_cas(gpr_atm *p, gpr_atm o, gpr_atm n) {
#ifdef GPR_ARCH_64
return o == (gpr_atm)InterlockedCompareExchangeAcquire64((volatile LONGLONG) p,
return o == (gpr_atm)InterlockedCompareExchangeAcquire64((volatile LONGLONG *) p,
(LONGLONG) n, (LONGLONG) o);
#else
return o == (gpr_atm)InterlockedCompareExchangeAcquire((volatile LONG *) p,

@ -45,8 +45,10 @@
#define GPR_WINSOCK_SOCKET 1
#ifdef __GNUC__
#define GPR_GCC_ATOMIC 1
#define GPR_GCC_TLS 1
#else
#define GPR_WIN32_ATOMIC 1
#define GPR_MSVC_TLS 1
#endif
#elif defined(_WIN32) || defined(WIN32)
#define GPR_ARCH_32 1
@ -55,14 +57,17 @@
#define GPR_WINSOCK_SOCKET 1
#ifdef __GNUC__
#define GPR_GCC_ATOMIC 1
#define GPR_GCC_TLS 1
#else
#define GPR_WIN32_ATOMIC 1
#define GPR_MSVC_TLS 1
#endif
#elif defined(ANDROID) || defined(__ANDROID__)
#define GPR_ANDROID 1
#define GPR_ARCH_32 1
#define GPR_CPU_LINUX 1
#define GPR_GCC_SYNC 1
#define GPR_GCC_TLS 1
#define GPR_POSIX_MULTIPOLL_WITH_POLL 1
#define GPR_POSIX_WAKEUP_FD 1
#define GPR_LINUX_EVENTFD 1
@ -88,6 +93,7 @@
#include <features.h>
#define GPR_CPU_LINUX 1
#define GPR_GCC_ATOMIC 1
#define GPR_GCC_TLS 1
#define GPR_LINUX 1
#define GPR_LINUX_MULTIPOLL_WITH_EPOLL 1
#define GPR_POSIX_WAKEUP_FD 1
@ -134,6 +140,32 @@
#define GPR_CPU_POSIX 1
#endif
#define GPR_GCC_ATOMIC 1
#define GPR_GCC_TLS 1
#define GPR_POSIX_LOG 1
#define GPR_POSIX_MULTIPOLL_WITH_POLL 1
#define GPR_POSIX_WAKEUP_FD 1
#define GPR_POSIX_NO_SPECIAL_WAKEUP_FD 1
#define GPR_POSIX_SOCKET 1
#define GPR_POSIX_SOCKETADDR 1
#define GPR_POSIX_SOCKETUTILS 1
#define GPR_POSIX_ENV 1
#define GPR_POSIX_FILE 1
#define GPR_POSIX_STRING 1
#define GPR_POSIX_SYNC 1
#define GPR_POSIX_TIME 1
#define GPR_GETPID_IN_UNISTD_H 1
#ifdef _LP64
#define GPR_ARCH_64 1
#else /* _LP64 */
#define GPR_ARCH_32 1
#endif /* _LP64 */
#elif defined(__FreeBSD__)
#ifndef _BSD_SOURCE
#define _BSD_SOURCE
#endif
#define GPR_CPU_POSIX 1
#define GPR_GCC_ATOMIC 1
#define GPR_GCC_TLS 1
#define GPR_POSIX_LOG 1
#define GPR_POSIX_MULTIPOLL_WITH_POLL 1
#define GPR_POSIX_WAKEUP_FD 1
@ -190,16 +222,20 @@
#error Must define exactly one of GPR_ARCH_32, GPR_ARCH_64
#endif
#if defined(GPR_CPU_LINUX) + defined(GPR_CPU_POSIX) + defined(GPR_WIN32) + defined(GPR_CPU_IPHONE) != 1
#error Must define exactly one of GPR_CPU_LINUX, GPR_CPU_POSIX, GPR_WIN32, GPR_CPU_IPHONE
#if defined(GPR_CPU_LINUX) + defined(GPR_CPU_POSIX) + defined(GPR_WIN32) + defined(GPR_CPU_IPHONE) + defined(GPR_CPU_CUSTOM) != 1
#error Must define exactly one of GPR_CPU_LINUX, GPR_CPU_POSIX, GPR_WIN32, GPR_CPU_IPHONE, GPR_CPU_CUSTOM
#endif
#if defined(GPR_POSIX_MULTIPOLL_WITH_POLL) && !defined(GPR_POSIX_SOCKET)
#error Must define GPR_POSIX_SOCKET to use GPR_POSIX_MULTIPOLL_WITH_POLL
#endif
#if defined(GPR_POSIX_SOCKET) + defined(GPR_WIN32) != 1
#error Must define exactly one of GPR_POSIX_SOCKET, GPR_WIN32
#if defined(GPR_POSIX_SOCKET) + defined(GPR_WINSOCK_SOCKET) + defined(GPR_CUSTOM_SOCKET) != 1
#error Must define exactly one of GPR_POSIX_SOCKET, GPR_WINSOCK_SOCKET, GPR_CUSTOM_SOCKET
#endif
#if defined(GPR_MSVC_TLS) + defined(GPR_GCC_TLS) + defined(GPR_PTHREAD_TLS) + defined(GPR_CUSTOM_TLS) != 1
#error Must define exactly one of GPR_MSVC_TLS, GPR_GCC_TLS, GPR_PTHREAD_TLS, GPR_CUSTOM_TLS
#endif
typedef int16_t gpr_int16;

@ -40,6 +40,8 @@
extern "C" {
#endif
#define GRPC_SLICE_BUFFER_INLINE_ELEMENTS 8
/* Represents an expandable array of slices, to be interpreted as a single item
TODO(ctiller): inline some small number of elements into the struct, to
avoid per-call allocations */
@ -52,6 +54,8 @@ typedef struct {
size_t capacity;
/* the combined length of all slices in the array */
size_t length;
/* inlined elements to avoid allocations */
gpr_slice inlined[GRPC_SLICE_BUFFER_INLINE_ELEMENTS];
} gpr_slice_buffer;
/* initialize a slice buffer */
@ -78,9 +82,11 @@ gpr_uint8 *gpr_slice_buffer_tiny_add(gpr_slice_buffer *sb, unsigned len);
void gpr_slice_buffer_pop(gpr_slice_buffer *sb);
/* clear a slice buffer, unref all elements */
void gpr_slice_buffer_reset_and_unref(gpr_slice_buffer *sb);
/* swap the contents of two slice buffers */
void gpr_slice_buffer_swap(gpr_slice_buffer *a, gpr_slice_buffer *b);
#ifdef __cplusplus
}
#endif
#endif /* GRPC_SUPPORT_SLICE_BUFFER_H */
#endif /* GRPC_SUPPORT_SLICE_BUFFER_H */

@ -60,7 +60,7 @@
#include <grpc/support/sync_posix.h>
#elif defined(GPR_WIN32)
#include <grpc/support/sync_win32.h>
#else
#elif !defined(GPR_CUSTOM_SYNC)
#error Unable to determine platform for sync
#endif

@ -31,16 +31,47 @@
*
*/
#ifndef MK1MF_BUILD
/* auto-generated by Configure for crypto/cversion.c:
* for Unix builds, crypto/Makefile.ssl generates functional definitions;
* Windows builds (and other mk1mf builds) compile cversion.c with
* -DMK1MF_BUILD and use definitions added to this file by util/mk1mf.pl. */
#error "Windows builds (PLATFORM=VC-WIN32) use mk1mf.pl-created Makefiles"
#ifndef GRPC_SUPPORT_TLS_H
#define GRPC_SUPPORT_TLS_H
#include "port_platform.h"
/* Thread local storage.
A minimal wrapper that should be implementable across many compilers,
and implementable efficiently across most modern compilers.
Thread locals have type gpr_intptr.
Declaring a thread local variable 'foo':
GPR_TLS_DECL(foo, initial_value);
Thread locals always have static scope.
Initializing a thread local (must be done at library initialization
time):
gpr_tls_init(&foo);
Destroying a thread local:
gpr_tls_destroy(&foo);
Setting a thread local:
gpr_tls_set(&foo, new_value);
Accessing a thread local:
current_value = gpr_tls_get(&foo, value);
ALL functions here may be implemented as macros. */
#ifdef GPR_GCC_TLS
#include "tls_gcc.h"
#endif
#ifdef GPR_MSVC_TLS
#include "tls_msvc.h"
#endif
#ifdef GPR_PTHREAD_TLS
#include "tls_pthread.h"
#endif
#ifdef MK1MF_PLATFORM_VC_WIN32
/* auto-generated/updated by util/mk1mf.pl for crypto/cversion.c */
#define CFLAGS "cl /MD /Ox /O2 /Ob2 -DOPENSSL_THREADS -DDSO_WIN32 -W3 -Gs0 -GF -Gy -nologo -DOPENSSL_SYSNAME_WIN32 -DWIN32_LEAN_AND_MEAN -DL_ENDIAN -D_CRT_SECURE_NO_DEPRECATE -DOPENSSL_USE_APPLINK -I. -DOPENSSL_NO_RC5 -DOPENSSL_NO_MD2 -DOPENSSL_NO_KRB5 -DOPENSSL_NO_JPAKE -DOPENSSL_NO_STATIC_ENGINE "
#define PLATFORM "VC-WIN32"
#define DATE "Sat Dec 13 01:17:07 2014"
#endif

@ -0,0 +1,52 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef GRPC_SUPPORT_TLS_GCC_H
#define GRPC_SUPPORT_TLS_GCC_H
/* Thread local storage based on gcc compiler primitives.
#include tls.h to use this - and see that file for documentation */
struct gpr_gcc_thread_local {
gpr_intptr value;
};
#define GPR_TLS_DECL(name) \
static __thread struct gpr_gcc_thread_local name = {0}
#define gpr_tls_init(tls) do {} while (0)
#define gpr_tls_destroy(tls) do {} while (0)
#define gpr_tls_set(tls, new_value) (((tls)->value) = (new_value))
#define gpr_tls_get(tls) ((tls)->value)
#endif

@ -0,0 +1,52 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef GRPC_SUPPORT_TLS_GCC_H
#define GRPC_SUPPORT_TLS_GCC_H
/* Thread local storage based on ms visual c compiler primitives.
#include tls.h to use this - and see that file for documentation */
struct gpr_msvc_thread_local {
gpr_intptr value;
};
#define GPR_TLS_DECL(name) \
static __thread struct gpr_msvc_thread_local name = {0}
#define gpr_tls_init(tls) do {} while (0)
#define gpr_tls_destroy(tls) do {} while (0)
#define gpr_tls_set(tls, new_value) (((tls)->value) = (new_value))
#define gpr_tls_get(tls) ((tls)->value)
#endif

@ -0,0 +1,53 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef GRPC_SUPPORT_TLS_PTHREAD_H
#define GRPC_SUPPORT_TLS_PTHREAD_H
/* Thread local storage based on pthread library calls.
#include tls.h to use this - and see that file for documentation */
struct gpr_pthread_thread_local {
pthread_key_t key;
};
#define GPR_TLS_DECL(name) \
static struct gpr_pthread_thread_local name = {0}
#define gpr_tls_init(tls) GPR_ASSERT(0 == pthread_key_create(&(tls)->key, NULL))
#define gpr_tls_destroy(tls) pthread_key_delete((tls)->key)
#define gpr_tls_set(tls, new_value) \
GPR_ASSERT(pthread_setspecific((tls)->key, (void*)(new_value)) == 0)
#define gpr_tls_get(tls) ((gpr_intptr)pthread_getspecific((tls)->key))
#endif

@ -110,7 +110,7 @@ bool HasBidiStreaming(const grpc::protobuf::FileDescriptor *file) {
return false;
}
grpc::string FilenameIdentifier(const grpc::string& filename) {
grpc::string FilenameIdentifier(const grpc::string &filename) {
grpc::string result;
for (unsigned i = 0; i < filename.size(); i++) {
char c = filename[i];
@ -154,6 +154,7 @@ grpc::string GetHeaderIncludes(const grpc::protobuf::FileDescriptor *file,
const Parameters &params) {
grpc::string temp =
"#include <grpc++/impl/internal_stub.h>\n"
"#include <grpc++/impl/rpc_method.h>\n"
"#include <grpc++/impl/service_type.h>\n"
"#include <grpc++/status.h>\n"
"\n"
@ -172,7 +173,9 @@ grpc::string GetHeaderIncludes(const grpc::protobuf::FileDescriptor *file,
temp.append("template <class OutMessage> class ClientWriter;\n");
temp.append("template <class InMessage> class ServerReader;\n");
temp.append("template <class OutMessage> class ClientAsyncWriter;\n");
temp.append("template <class OutMessage, class InMessage> class ServerAsyncReader;\n");
temp.append(
"template <class OutMessage, class InMessage> class "
"ServerAsyncReader;\n");
}
if (HasServerOnlyStreaming(file)) {
temp.append("template <class InMessage> class ClientReader;\n");
@ -246,11 +249,11 @@ void PrintHeaderClientMethod(grpc::protobuf::io::Printer *printer,
*vars,
"std::unique_ptr< ::grpc::ClientReader< $Response$>> $Method$("
"::grpc::ClientContext* context, const $Request$& request);\n");
printer->Print(
*vars,
"std::unique_ptr< ::grpc::ClientAsyncReader< $Response$>> Async$Method$("
"::grpc::ClientContext* context, const $Request$& request, "
"::grpc::CompletionQueue* cq, void* tag);\n");
printer->Print(*vars,
"std::unique_ptr< ::grpc::ClientAsyncReader< $Response$>> "
"Async$Method$("
"::grpc::ClientContext* context, const $Request$& request, "
"::grpc::CompletionQueue* cq, void* tag);\n");
} else if (BidiStreaming(method)) {
printer->Print(
*vars,
@ -264,10 +267,16 @@ void PrintHeaderClientMethod(grpc::protobuf::io::Printer *printer,
}
}
void PrintHeaderServerMethodSync(
grpc::protobuf::io::Printer *printer,
const grpc::protobuf::MethodDescriptor *method,
std::map<grpc::string, grpc::string> *vars) {
void PrintHeaderClientMethodData(grpc::protobuf::io::Printer *printer,
const grpc::protobuf::MethodDescriptor *method,
std::map<grpc::string, grpc::string> *vars) {
(*vars)["Method"] = method->name();
printer->Print(*vars, "const ::grpc::RpcMethod rpcmethod_$Method$_;\n");
}
void PrintHeaderServerMethodSync(grpc::protobuf::io::Printer *printer,
const grpc::protobuf::MethodDescriptor *method,
std::map<grpc::string, grpc::string> *vars) {
(*vars)["Method"] = method->name();
(*vars)["Request"] =
grpc_cpp_generator::ClassName(method->input_type(), true);
@ -351,10 +360,18 @@ void PrintHeaderService(grpc::protobuf::io::Printer *printer,
"class Stub GRPC_FINAL : public ::grpc::InternalStub {\n"
" public:\n");
printer->Indent();
printer->Print(
"Stub(const std::shared_ptr< ::grpc::ChannelInterface>& channel);\n");
for (int i = 0; i < service->method_count(); ++i) {
PrintHeaderClientMethod(printer, service->method(i), vars);
}
printer->Outdent();
printer->Print(" private:\n");
printer->Indent();
for (int i = 0; i < service->method_count(); ++i) {
PrintHeaderClientMethodData(printer, service->method(i), vars);
}
printer->Outdent();
printer->Print("};\n");
printer->Print(
"static std::unique_ptr<Stub> NewStub(const std::shared_ptr< "
@ -479,7 +496,6 @@ grpc::string GetSourceIncludes(const grpc::protobuf::FileDescriptor *file,
printer.Print(vars, "#include <grpc++/async_unary_call.h>\n");
printer.Print(vars, "#include <grpc++/channel_interface.h>\n");
printer.Print(vars, "#include <grpc++/impl/client_unary_call.h>\n");
printer.Print(vars, "#include <grpc++/impl/rpc_method.h>\n");
printer.Print(vars, "#include <grpc++/impl/rpc_service_method.h>\n");
printer.Print(vars, "#include <grpc++/impl/service_type.h>\n");
printer.Print(vars, "#include <grpc++/stream.h>\n");
@ -513,8 +529,8 @@ void PrintSourceClientMethod(grpc::protobuf::io::Printer *printer,
"::grpc::ClientContext* context, "
"const $Request$& request, $Response$* response) {\n");
printer->Print(*vars,
" return ::grpc::BlockingUnaryCall(channel(),"
"::grpc::RpcMethod($prefix$$Service$_method_names[$Idx$]), "
" return ::grpc::BlockingUnaryCall(channel(), "
"rpcmethod_$Method$_, "
"context, request, response);\n"
"}\n\n");
printer->Print(
@ -528,7 +544,7 @@ void PrintSourceClientMethod(grpc::protobuf::io::Printer *printer,
"::grpc::ClientAsyncResponseReader< $Response$>>(new "
"::grpc::ClientAsyncResponseReader< $Response$>("
"channel(), cq, "
"::grpc::RpcMethod($prefix$$Service$_method_names[$Idx$]), "
"rpcmethod_$Method$_, "
"context, request, tag));\n"
"}\n\n");
} else if (ClientOnlyStreaming(method)) {
@ -540,8 +556,7 @@ void PrintSourceClientMethod(grpc::protobuf::io::Printer *printer,
" return std::unique_ptr< ::grpc::ClientWriter< "
"$Request$>>(new ::grpc::ClientWriter< $Request$>("
"channel(),"
"::grpc::RpcMethod($prefix$$Service$_method_names[$Idx$], "
"::grpc::RpcMethod::RpcType::CLIENT_STREAMING), "
"rpcmethod_$Method$_, "
"context, response));\n"
"}\n\n");
printer->Print(*vars,
@ -553,8 +568,7 @@ void PrintSourceClientMethod(grpc::protobuf::io::Printer *printer,
" return std::unique_ptr< ::grpc::ClientAsyncWriter< "
"$Request$>>(new ::grpc::ClientAsyncWriter< $Request$>("
"channel(), cq, "
"::grpc::RpcMethod($prefix$$Service$_method_names[$Idx$], "
"::grpc::RpcMethod::RpcType::CLIENT_STREAMING), "
"rpcmethod_$Method$_, "
"context, response, tag));\n"
"}\n\n");
} else if (ServerOnlyStreaming(method)) {
@ -567,8 +581,7 @@ void PrintSourceClientMethod(grpc::protobuf::io::Printer *printer,
" return std::unique_ptr< ::grpc::ClientReader< "
"$Response$>>(new ::grpc::ClientReader< $Response$>("
"channel(),"
"::grpc::RpcMethod($prefix$$Service$_method_names[$Idx$], "
"::grpc::RpcMethod::RpcType::SERVER_STREAMING), "
"rpcmethod_$Method$_, "
"context, request));\n"
"}\n\n");
printer->Print(*vars,
@ -580,8 +593,7 @@ void PrintSourceClientMethod(grpc::protobuf::io::Printer *printer,
" return std::unique_ptr< ::grpc::ClientAsyncReader< "
"$Response$>>(new ::grpc::ClientAsyncReader< $Response$>("
"channel(), cq, "
"::grpc::RpcMethod($prefix$$Service$_method_names[$Idx$], "
"::grpc::RpcMethod::RpcType::SERVER_STREAMING), "
"rpcmethod_$Method$_, "
"context, request, tag));\n"
"}\n\n");
} else if (BidiStreaming(method)) {
@ -594,22 +606,21 @@ void PrintSourceClientMethod(grpc::protobuf::io::Printer *printer,
"$Request$, $Response$>>(new ::grpc::ClientReaderWriter< "
"$Request$, $Response$>("
"channel(),"
"::grpc::RpcMethod($prefix$$Service$_method_names[$Idx$], "
"::grpc::RpcMethod::RpcType::BIDI_STREAMING), "
"rpcmethod_$Method$_, "
"context));\n"
"}\n\n");
printer->Print(*vars,
"std::unique_ptr< ::grpc::ClientAsyncReaderWriter< "
"$Request$, $Response$>> "
"$ns$$Service$::Stub::Async$Method$(::grpc::ClientContext* context, "
"::grpc::CompletionQueue* cq, void* tag) {\n");
printer->Print(
*vars,
"std::unique_ptr< ::grpc::ClientAsyncReaderWriter< "
"$Request$, $Response$>> "
"$ns$$Service$::Stub::Async$Method$(::grpc::ClientContext* context, "
"::grpc::CompletionQueue* cq, void* tag) {\n");
printer->Print(*vars,
" return std::unique_ptr< ::grpc::ClientAsyncReaderWriter< "
"$Request$, $Response$>>(new "
"::grpc::ClientAsyncReaderWriter< $Request$, $Response$>("
"channel(), cq, "
"::grpc::RpcMethod($prefix$$Service$_method_names[$Idx$], "
"::grpc::RpcMethod::RpcType::BIDI_STREAMING), "
"rpcmethod_$Method$_, "
"context, tag));\n"
"}\n\n");
}
@ -681,9 +692,9 @@ void PrintSourceServerAsyncMethod(
"$Request$* request, "
"::grpc::ServerAsyncResponseWriter< $Response$>* response, "
"::grpc::CompletionQueue* cq, void* tag) {\n");
printer->Print(
*vars,
" AsynchronousService::RequestAsyncUnary($Idx$, context, request, response, cq, tag);\n");
printer->Print(*vars,
" AsynchronousService::RequestAsyncUnary($Idx$, context, "
"request, response, cq, tag);\n");
printer->Print("}\n\n");
} else if (ClientOnlyStreaming(method)) {
printer->Print(*vars,
@ -691,9 +702,9 @@ void PrintSourceServerAsyncMethod(
"::grpc::ServerContext* context, "
"::grpc::ServerAsyncReader< $Response$, $Request$>* reader, "
"::grpc::CompletionQueue* cq, void* tag) {\n");
printer->Print(
*vars,
" AsynchronousService::RequestClientStreaming($Idx$, context, reader, cq, tag);\n");
printer->Print(*vars,
" AsynchronousService::RequestClientStreaming($Idx$, "
"context, reader, cq, tag);\n");
printer->Print("}\n\n");
} else if (ServerOnlyStreaming(method)) {
printer->Print(*vars,
@ -702,9 +713,9 @@ void PrintSourceServerAsyncMethod(
"$Request$* request, "
"::grpc::ServerAsyncWriter< $Response$>* writer, "
"::grpc::CompletionQueue* cq, void* tag) {\n");
printer->Print(
*vars,
" AsynchronousService::RequestServerStreaming($Idx$, context, request, writer, cq, tag);\n");
printer->Print(*vars,
" AsynchronousService::RequestServerStreaming($Idx$, "
"context, request, writer, cq, tag);\n");
printer->Print("}\n\n");
} else if (BidiStreaming(method)) {
printer->Print(
@ -713,9 +724,9 @@ void PrintSourceServerAsyncMethod(
"::grpc::ServerContext* context, "
"::grpc::ServerAsyncReaderWriter< $Response$, $Request$>* stream, "
"::grpc::CompletionQueue* cq, void *tag) {\n");
printer->Print(
*vars,
" AsynchronousService::RequestBidiStreaming($Idx$, context, stream, cq, tag);\n");
printer->Print(*vars,
" AsynchronousService::RequestBidiStreaming($Idx$, "
"context, stream, cq, tag);\n");
printer->Print("}\n\n");
}
}
@ -725,7 +736,8 @@ void PrintSourceService(grpc::protobuf::io::Printer *printer,
std::map<grpc::string, grpc::string> *vars) {
(*vars)["Service"] = service->name();
printer->Print(*vars, "static const char* $prefix$$Service$_method_names[] = {\n");
printer->Print(*vars,
"static const char* $prefix$$Service$_method_names[] = {\n");
for (int i = 0; i < service->method_count(); ++i) {
(*vars)["Method"] = service->method(i)->name();
printer->Print(*vars, " \"/$Package$$Service$/$Method$\",\n");
@ -736,21 +748,51 @@ void PrintSourceService(grpc::protobuf::io::Printer *printer,
*vars,
"std::unique_ptr< $ns$$Service$::Stub> $ns$$Service$::NewStub("
"const std::shared_ptr< ::grpc::ChannelInterface>& channel) {\n"
" std::unique_ptr< $ns$$Service$::Stub> stub(new $ns$$Service$::Stub());\n"
" stub->set_channel(channel);\n"
" std::unique_ptr< $ns$$Service$::Stub> stub(new "
"$ns$$Service$::Stub(channel));\n"
" return stub;\n"
"}\n\n");
printer->Print(*vars,
"$ns$$Service$::Stub::Stub(const std::shared_ptr< "
"::grpc::ChannelInterface>& channel)\n");
printer->Indent();
printer->Print(": ::grpc::InternalStub(channel)");
for (int i = 0; i < service->method_count(); ++i) {
const grpc::protobuf::MethodDescriptor *method = service->method(i);
(*vars)["Method"] = method->name();
(*vars)["Idx"] = as_string(i);
if (NoStreaming(method)) {
(*vars)["StreamingType"] = "NORMAL_RPC";
} else if (ClientOnlyStreaming(method)) {
(*vars)["StreamingType"] = "CLIENT_STREAMING";
} else if (ServerOnlyStreaming(method)) {
(*vars)["StreamingType"] = "SERVER_STREAMING";
} else {
(*vars)["StreamingType"] = "BIDI_STREAMING";
}
printer->Print(
*vars,
", rpcmethod_$Method$_("
"$prefix$$Service$_method_names[$Idx$], "
"::grpc::RpcMethod::$StreamingType$, "
"channel->RegisterMethod($prefix$$Service$_method_names[$Idx$])"
")\n");
}
printer->Print("{}\n\n");
printer->Outdent();
for (int i = 0; i < service->method_count(); ++i) {
(*vars)["Idx"] = as_string(i);
PrintSourceClientMethod(printer, service->method(i), vars);
}
(*vars)["MethodCount"] = as_string(service->method_count());
printer->Print(
*vars,
"$ns$$Service$::AsyncService::AsyncService(::grpc::CompletionQueue* cq) : "
"::grpc::AsynchronousService(cq, $prefix$$Service$_method_names, $MethodCount$) "
"{}\n\n");
printer->Print(*vars,
"$ns$$Service$::AsyncService::AsyncService(::grpc::"
"CompletionQueue* cq) : "
"::grpc::AsynchronousService(cq, "
"$prefix$$Service$_method_names, $MethodCount$) "
"{}\n\n");
printer->Print(*vars,
"$ns$$Service$::Service::~Service() {\n"
@ -783,7 +825,8 @@ void PrintSourceService(grpc::protobuf::io::Printer *printer,
"service_->AddMethod(new ::grpc::RpcServiceMethod(\n"
" $prefix$$Service$_method_names[$Idx$],\n"
" ::grpc::RpcMethod::NORMAL_RPC,\n"
" new ::grpc::RpcMethodHandler< $ns$$Service$::Service, $Request$, "
" new ::grpc::RpcMethodHandler< $ns$$Service$::Service, "
"$Request$, "
"$Response$>(\n"
" std::function< ::grpc::Status($ns$$Service$::Service*, "
"::grpc::ServerContext*, const $Request$*, $Response$*)>("

@ -43,12 +43,27 @@
static void put_metadata(gpr_strvec *b, grpc_mdelem *md) {
gpr_strvec_add(b, gpr_strdup(" key="));
gpr_strvec_add(b, gpr_hexdump((char *)GPR_SLICE_START_PTR(md->key->slice),
GPR_SLICE_LENGTH(md->key->slice), GPR_HEXDUMP_PLAINTEXT));
gpr_strvec_add(
b, gpr_hexdump((char *)GPR_SLICE_START_PTR(md->key->slice),
GPR_SLICE_LENGTH(md->key->slice), GPR_HEXDUMP_PLAINTEXT));
gpr_strvec_add(b, gpr_strdup(" value="));
gpr_strvec_add(b, gpr_hexdump((char *)GPR_SLICE_START_PTR(md->value->slice),
GPR_SLICE_LENGTH(md->value->slice), GPR_HEXDUMP_PLAINTEXT));
GPR_SLICE_LENGTH(md->value->slice),
GPR_HEXDUMP_PLAINTEXT));
}
static void put_metadata_list(gpr_strvec *b, grpc_metadata_batch md) {
grpc_linked_mdelem *m;
for (m = md.list.head; m != NULL; m = m->next) {
put_metadata(b, m->md);
}
if (gpr_time_cmp(md.deadline, gpr_inf_future) != 0) {
char *tmp;
gpr_asprintf(&tmp, " deadline=%d.%09d", md.deadline.tv_sec,
md.deadline.tv_nsec);
gpr_strvec_add(b, tmp);
}
}
char *grpc_call_op_string(grpc_call_op *op) {
@ -69,16 +84,7 @@ char *grpc_call_op_string(grpc_call_op *op) {
switch (op->type) {
case GRPC_SEND_METADATA:
gpr_strvec_add(&b, gpr_strdup("SEND_METADATA"));
put_metadata(&b, op->data.metadata);
break;
case GRPC_SEND_DEADLINE:
gpr_asprintf(&tmp, "SEND_DEADLINE %d.%09d", op->data.deadline.tv_sec,
op->data.deadline.tv_nsec);
gpr_strvec_add(&b, tmp);
break;
case GRPC_SEND_START:
gpr_asprintf(&tmp, "SEND_START pollset=%p", op->data.start.pollset);
gpr_strvec_add(&b, tmp);
put_metadata_list(&b, op->data.metadata);
break;
case GRPC_SEND_MESSAGE:
gpr_strvec_add(&b, gpr_strdup("SEND_MESSAGE"));
@ -94,15 +100,7 @@ char *grpc_call_op_string(grpc_call_op *op) {
break;
case GRPC_RECV_METADATA:
gpr_strvec_add(&b, gpr_strdup("RECV_METADATA"));
put_metadata(&b, op->data.metadata);
break;
case GRPC_RECV_DEADLINE:
gpr_asprintf(&tmp, "RECV_DEADLINE %d.%09d", op->data.deadline.tv_sec,
op->data.deadline.tv_nsec);
gpr_strvec_add(&b, tmp);
break;
case GRPC_RECV_END_OF_INITIAL_METADATA:
gpr_strvec_add(&b, gpr_strdup("RECV_END_OF_INITIAL_METADATA"));
put_metadata_list(&b, op->data.metadata);
break;
case GRPC_RECV_MESSAGE:
gpr_strvec_add(&b, gpr_strdup("RECV_MESSAGE"));
@ -113,12 +111,21 @@ char *grpc_call_op_string(grpc_call_op *op) {
case GRPC_RECV_FINISH:
gpr_strvec_add(&b, gpr_strdup("RECV_FINISH"));
break;
case GRPC_RECV_SYNTHETIC_STATUS:
gpr_asprintf(&tmp, "RECV_SYNTHETIC_STATUS status=%d message='%s'",
op->data.synthetic_status.status,
op->data.synthetic_status.message);
gpr_strvec_add(&b, tmp);
break;
case GRPC_CANCEL_OP:
gpr_strvec_add(&b, gpr_strdup("CANCEL_OP"));
break;
}
gpr_asprintf(&tmp, " flags=0x%08x", op->flags);
gpr_strvec_add(&b, tmp);
if (op->bind_pollset) {
gpr_strvec_add(&b, gpr_strdup("bind_pollset"));
}
out = gpr_strvec_flatten(&b, NULL);
gpr_strvec_destroy(&b);

@ -62,11 +62,13 @@ static void init_rpc_stats(census_rpc_stats* stats) {
static void extract_and_annotate_method_tag(grpc_call_op* op, call_data* calld,
channel_data* chand) {
if (op->data.metadata->key == chand->path_str) {
gpr_log(GPR_DEBUG,
(const char*)GPR_SLICE_START_PTR(op->data.metadata->value->slice));
census_add_method_tag(calld->op_id, (const char*)GPR_SLICE_START_PTR(
op->data.metadata->value->slice));
grpc_linked_mdelem* m;
for (m = op->data.metadata.list.head; m != NULL; m = m->next) {
if (m->md->key == chand->path_str) {
gpr_log(GPR_DEBUG, "%s", (const char*)GPR_SLICE_START_PTR(m->md->value->slice));
census_add_method_tag(
calld->op_id, (const char*)GPR_SLICE_START_PTR(m->md->value->slice));
}
}
}
@ -178,11 +180,11 @@ static void destroy_channel_elem(grpc_channel_element* elem) {
}
const grpc_channel_filter grpc_client_census_filter = {
client_call_op, channel_op, sizeof(call_data),
client_init_call_elem, client_destroy_call_elem, sizeof(channel_data),
init_channel_elem, destroy_channel_elem, "census-client"};
client_call_op, channel_op, sizeof(call_data), client_init_call_elem,
client_destroy_call_elem, sizeof(channel_data), init_channel_elem,
destroy_channel_elem, "census-client"};
const grpc_channel_filter grpc_server_census_filter = {
server_call_op, channel_op, sizeof(call_data),
server_init_call_elem, server_destroy_call_elem, sizeof(channel_data),
init_channel_elem, destroy_channel_elem, "census-server"};
server_call_op, channel_op, sizeof(call_data), server_init_call_elem,
server_destroy_call_elem, sizeof(channel_data), init_channel_elem,
destroy_channel_elem, "census-server"};

@ -77,9 +77,9 @@ size_t grpc_channel_stack_size(const grpc_channel_filter **filters,
return size;
}
#define CHANNEL_ELEMS_FROM_STACK(stk) \
((grpc_channel_element *)( \
(char *)(stk) + ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_channel_stack))))
#define CHANNEL_ELEMS_FROM_STACK(stk) \
((grpc_channel_element *)((char *)(stk) + ROUND_UP_TO_ALIGNMENT_SIZE( \
sizeof(grpc_channel_stack))))
#define CALL_ELEMS_FROM_STACK(stk) \
((grpc_call_element *)((char *)(stk) + \
@ -183,6 +183,9 @@ void grpc_call_stack_destroy(grpc_call_stack *stack) {
void grpc_call_next_op(grpc_call_element *elem, grpc_call_op *op) {
grpc_call_element *next_elem = elem + op->dir;
if (op->type == GRPC_SEND_METADATA || op->type == GRPC_RECV_METADATA) {
grpc_metadata_batch_assert_ok(&op->data.metadata);
}
next_elem->filter->call_op(next_elem, elem, op);
}
@ -193,42 +196,17 @@ void grpc_channel_next_op(grpc_channel_element *elem, grpc_channel_op *op) {
grpc_channel_stack *grpc_channel_stack_from_top_element(
grpc_channel_element *elem) {
return (grpc_channel_stack *)((char *)(elem) -
ROUND_UP_TO_ALIGNMENT_SIZE(
sizeof(grpc_channel_stack)));
return (grpc_channel_stack *)((char *)(elem)-ROUND_UP_TO_ALIGNMENT_SIZE(
sizeof(grpc_channel_stack)));
}
grpc_call_stack *grpc_call_stack_from_top_element(grpc_call_element *elem) {
return (grpc_call_stack *)((char *)(elem) - ROUND_UP_TO_ALIGNMENT_SIZE(
sizeof(grpc_call_stack)));
return (grpc_call_stack *)((char *)(elem)-ROUND_UP_TO_ALIGNMENT_SIZE(
sizeof(grpc_call_stack)));
}
static void do_nothing(void *user_data, grpc_op_error error) {}
void grpc_call_element_recv_metadata(grpc_call_element *cur_elem,
grpc_mdelem *mdelem) {
grpc_call_op metadata_op;
metadata_op.type = GRPC_RECV_METADATA;
metadata_op.dir = GRPC_CALL_UP;
metadata_op.done_cb = do_nothing;
metadata_op.user_data = NULL;
metadata_op.flags = 0;
metadata_op.data.metadata = mdelem;
grpc_call_next_op(cur_elem, &metadata_op);
}
void grpc_call_element_send_metadata(grpc_call_element *cur_elem,
grpc_mdelem *mdelem) {
grpc_call_op metadata_op;
metadata_op.type = GRPC_SEND_METADATA;
metadata_op.dir = GRPC_CALL_DOWN;
metadata_op.done_cb = do_nothing;
metadata_op.user_data = NULL;
metadata_op.flags = 0;
metadata_op.data.metadata = mdelem;
grpc_call_next_op(cur_elem, &metadata_op);
}
void grpc_call_element_send_cancel(grpc_call_element *cur_elem) {
grpc_call_op cancel_op;
cancel_op.type = GRPC_CANCEL_OP;
@ -236,6 +214,7 @@ void grpc_call_element_send_cancel(grpc_call_element *cur_elem) {
cancel_op.done_cb = do_nothing;
cancel_op.user_data = NULL;
cancel_op.flags = 0;
cancel_op.bind_pollset = NULL;
grpc_call_next_op(cur_elem, &cancel_op);
}
@ -246,5 +225,19 @@ void grpc_call_element_send_finish(grpc_call_element *cur_elem) {
finish_op.done_cb = do_nothing;
finish_op.user_data = NULL;
finish_op.flags = 0;
finish_op.bind_pollset = NULL;
grpc_call_next_op(cur_elem, &finish_op);
}
void grpc_call_element_recv_status(grpc_call_element *cur_elem,
grpc_status_code status,
const char *message) {
grpc_call_op op;
op.type = GRPC_RECV_SYNTHETIC_STATUS;
op.dir = GRPC_CALL_UP;
op.done_cb = do_nothing;
op.user_data = NULL;
op.data.synthetic_status.status = status;
op.data.synthetic_status.message = message;
grpc_call_next_op(cur_elem, &op);
}

@ -62,10 +62,6 @@ typedef struct grpc_call_element grpc_call_element;
typedef enum {
/* send metadata to the channels peer */
GRPC_SEND_METADATA,
/* send a deadline */
GRPC_SEND_DEADLINE,
/* start a connection (corresponds to start_invoke/accept) */
GRPC_SEND_START,
/* send a message to the channels peer */
GRPC_SEND_MESSAGE,
/* send a pre-formatted message to the channels peer */
@ -76,16 +72,14 @@ typedef enum {
GRPC_REQUEST_DATA,
/* metadata was received from the channels peer */
GRPC_RECV_METADATA,
/* receive a deadline */
GRPC_RECV_DEADLINE,
/* the end of the first batch of metadata was received */
GRPC_RECV_END_OF_INITIAL_METADATA,
/* a message was received from the channels peer */
GRPC_RECV_MESSAGE,
/* half-close was received from the channels peer */
GRPC_RECV_HALF_CLOSE,
/* full close was received from the channels peer */
GRPC_RECV_FINISH,
/* a status has been sythesized locally */
GRPC_RECV_SYNTHETIC_STATUS,
/* the call has been abnormally terminated */
GRPC_CANCEL_OP
} grpc_call_op_type;
@ -109,14 +103,16 @@ typedef struct {
/* Argument data, matching up with grpc_call_op_type names */
union {
struct {
grpc_pollset *pollset;
} start;
grpc_byte_buffer *message;
grpc_mdelem *metadata;
gpr_timespec deadline;
grpc_metadata_batch metadata;
struct {
grpc_status_code status;
const char *message;
} synthetic_status;
} data;
grpc_pollset *bind_pollset;
/* Must be called when processing of this call-op is complete.
Signature chosen to match transport flow control callbacks */
void (*done_cb)(void *user_data, grpc_op_error error);
@ -291,16 +287,15 @@ grpc_call_stack *grpc_call_stack_from_top_element(grpc_call_element *elem);
void grpc_call_log_op(char *file, int line, gpr_log_severity severity,
grpc_call_element *elem, grpc_call_op *op);
void grpc_call_element_send_metadata(grpc_call_element *cur_elem,
grpc_mdelem *elem);
void grpc_call_element_recv_metadata(grpc_call_element *cur_elem,
grpc_mdelem *elem);
void grpc_call_element_send_cancel(grpc_call_element *cur_elem);
void grpc_call_element_send_finish(grpc_call_element *cur_elem);
void grpc_call_element_recv_status(grpc_call_element *cur_elem,
grpc_status_code status,
const char *message);
extern int grpc_trace_channel;
#define GRPC_CALL_LOG_OP(sev, elem, op) \
if (grpc_trace_channel) grpc_call_log_op(sev, elem, op)
#endif /* GRPC_INTERNAL_CORE_CHANNEL_CHANNEL_STACK_H */
#endif /* GRPC_INTERNAL_CORE_CHANNEL_CHANNEL_STACK_H */

@ -38,7 +38,6 @@
#include "src/core/channel/channel_args.h"
#include "src/core/channel/child_channel.h"
#include "src/core/channel/connected_channel.h"
#include "src/core/channel/metadata_buffer.h"
#include "src/core/iomgr/iomgr.h"
#include "src/core/support/string.h"
#include <grpc/support/alloc.h>
@ -70,9 +69,6 @@ typedef struct {
int transport_setup_initiated;
grpc_channel_args *args;
/* metadata cache */
grpc_mdelem *cancel_status;
} channel_data;
typedef enum {
@ -86,20 +82,16 @@ struct call_data {
/* owning element */
grpc_call_element *elem;
gpr_uint8 got_first_send;
call_state state;
grpc_metadata_buffer pending_metadata;
gpr_timespec deadline;
union {
struct {
/* our child call stack */
grpc_child_call *child_call;
} active;
struct {
void (*on_complete)(void *user_data, grpc_op_error error);
void *on_complete_user_data;
gpr_uint32 start_flags;
grpc_pollset *pollset;
} waiting;
grpc_call_op waiting_op;
} s;
};
@ -127,20 +119,6 @@ static void complete_activate(grpc_call_element *elem, grpc_call_op *op) {
GPR_ASSERT(calld->state == CALL_ACTIVE);
/* sending buffered metadata down the stack before the start call */
grpc_metadata_buffer_flush(&calld->pending_metadata, child_elem);
if (gpr_time_cmp(calld->deadline, gpr_inf_future) != 0) {
grpc_call_op dop;
dop.type = GRPC_SEND_DEADLINE;
dop.dir = GRPC_CALL_DOWN;
dop.flags = 0;
dop.data.deadline = calld->deadline;
dop.done_cb = do_nothing;
dop.user_data = NULL;
child_elem->filter->call_op(child_elem, elem, &dop);
}
/* continue the start call down the stack, this nees to happen after metadata
are flushed*/
child_elem->filter->call_op(child_elem, elem, op);
@ -152,6 +130,7 @@ static void start_rpc(grpc_call_element *elem, grpc_call_op *op) {
gpr_mu_lock(&chand->mu);
if (calld->state == CALL_CANCELLED) {
gpr_mu_unlock(&chand->mu);
grpc_metadata_batch_destroy(&op->data.metadata);
op->done_cb(op->user_data, GRPC_OP_ERROR);
return;
}
@ -184,10 +163,7 @@ static void start_rpc(grpc_call_element *elem, grpc_call_op *op) {
gpr_realloc(chand->waiting_children,
chand->waiting_child_capacity * sizeof(call_data *));
}
calld->s.waiting.on_complete = op->done_cb;
calld->s.waiting.on_complete_user_data = op->user_data;
calld->s.waiting.start_flags = op->flags;
calld->s.waiting.pollset = op->data.start.pollset;
calld->s.waiting_op = *op;
chand->waiting_children[chand->waiting_child_count++] = calld;
gpr_mu_unlock(&chand->mu);
@ -212,15 +188,8 @@ static void remove_waiting_child(channel_data *chand, call_data *calld) {
static void send_up_cancelled_ops(grpc_call_element *elem) {
grpc_call_op finish_op;
channel_data *chand = elem->channel_data;
/* send up a synthesized status */
finish_op.type = GRPC_RECV_METADATA;
finish_op.dir = GRPC_CALL_UP;
finish_op.flags = 0;
finish_op.data.metadata = grpc_mdelem_ref(chand->cancel_status);
finish_op.done_cb = do_nothing;
finish_op.user_data = NULL;
grpc_call_next_op(elem, &finish_op);
grpc_call_element_recv_status(elem, GRPC_STATUS_CANCELLED, "Cancelled");
/* send up a finish */
finish_op.type = GRPC_RECV_FINISH;
finish_op.dir = GRPC_CALL_UP;
@ -243,12 +212,12 @@ static void cancel_rpc(grpc_call_element *elem, grpc_call_op *op) {
child_elem->filter->call_op(child_elem, elem, op);
return; /* early out */
case CALL_WAITING:
grpc_metadata_batch_destroy(&calld->s.waiting_op.data.metadata);
remove_waiting_child(chand, calld);
calld->state = CALL_CANCELLED;
gpr_mu_unlock(&chand->mu);
send_up_cancelled_ops(elem);
calld->s.waiting.on_complete(calld->s.waiting.on_complete_user_data,
GRPC_OP_ERROR);
calld->s.waiting_op.done_cb(calld->s.waiting_op.user_data, GRPC_OP_ERROR);
return; /* early out */
case CALL_CREATED:
calld->state = CALL_CANCELLED;
@ -271,15 +240,13 @@ static void call_op(grpc_call_element *elem, grpc_call_element *from_elem,
switch (op->type) {
case GRPC_SEND_METADATA:
grpc_metadata_buffer_queue(&calld->pending_metadata, op);
break;
case GRPC_SEND_DEADLINE:
calld->deadline = op->data.deadline;
op->done_cb(op->user_data, GRPC_OP_OK);
break;
case GRPC_SEND_START:
/* filter out the start event to find which child to send on */
start_rpc(elem, op);
if (!calld->got_first_send) {
/* filter out the start event to find which child to send on */
calld->got_first_send = 1;
start_rpc(elem, op);
} else {
grpc_call_next_op(elem, op);
}
break;
case GRPC_CANCEL_OP:
cancel_rpc(elem, op);
@ -382,12 +349,6 @@ static void channel_op(grpc_channel_element *elem,
}
}
static void error_bad_on_complete(void *arg, grpc_op_error error) {
gpr_log(GPR_ERROR,
"Waiting finished but not started? Bad on_complete callback");
abort();
}
/* Constructor for call_data */
static void init_call_elem(grpc_call_element *elem,
const void *server_transport_data) {
@ -398,23 +359,22 @@ static void init_call_elem(grpc_call_element *elem,
calld->elem = elem;
calld->state = CALL_CREATED;
calld->deadline = gpr_inf_future;
calld->s.waiting.on_complete = error_bad_on_complete;
calld->s.waiting.on_complete_user_data = NULL;
grpc_metadata_buffer_init(&calld->pending_metadata);
calld->got_first_send = 0;
}
/* Destructor for call_data */
static void destroy_call_elem(grpc_call_element *elem) {
call_data *calld = elem->call_data;
/* if the metadata buffer is not flushed, destroy it here. */
grpc_metadata_buffer_destroy(&calld->pending_metadata, GRPC_OP_OK);
/* if the call got activated, we need to destroy the child stack also, and
remove it from the in-flight requests tracked by the child_entry we
picked */
if (calld->state == CALL_ACTIVE) {
grpc_child_call_destroy(calld->s.active.child_call);
}
if (calld->state == CALL_WAITING) {
grpc_metadata_batch_destroy(&calld->s.waiting_op.data.metadata);
}
}
/* Constructor for channel_data */
@ -423,7 +383,6 @@ static void init_channel_elem(grpc_channel_element *elem,
grpc_mdctx *metadata_context, int is_first,
int is_last) {
channel_data *chand = elem->channel_data;
char temp[GPR_LTOA_MIN_BUFSIZE];
GPR_ASSERT(!is_first);
GPR_ASSERT(is_last);
@ -437,10 +396,6 @@ static void init_channel_elem(grpc_channel_element *elem,
chand->transport_setup = NULL;
chand->transport_setup_initiated = 0;
chand->args = grpc_channel_args_copy(args);
gpr_ltoa(GRPC_STATUS_CANCELLED, temp);
chand->cancel_status =
grpc_mdelem_from_strings(metadata_context, "grpc-status", temp);
}
/* Destructor for channel_data */
@ -455,7 +410,6 @@ static void destroy_channel_elem(grpc_channel_element *elem) {
}
grpc_channel_args_destroy(chand->args);
grpc_mdelem_unref(chand->cancel_status);
gpr_mu_destroy(&chand->mu);
GPR_ASSERT(chand->waiting_child_count == 0);
@ -463,9 +417,10 @@ static void destroy_channel_elem(grpc_channel_element *elem) {
}
const grpc_channel_filter grpc_client_channel_filter = {
call_op, channel_op, sizeof(call_data),
init_call_elem, destroy_call_elem, sizeof(channel_data),
init_channel_elem, destroy_channel_elem, "client-channel", };
call_op, channel_op, sizeof(call_data), init_call_elem, destroy_call_elem,
sizeof(channel_data), init_channel_elem, destroy_channel_elem,
"client-channel",
};
grpc_transport_setup_result grpc_client_channel_transport_setup_complete(
grpc_channel_stack *channel_stack, grpc_transport *transport,
@ -520,13 +475,7 @@ grpc_transport_setup_result grpc_client_channel_transport_setup_complete(
call_ops = gpr_malloc(sizeof(grpc_call_op) * waiting_child_count);
for (i = 0; i < waiting_child_count; i++) {
call_ops[i].type = GRPC_SEND_START;
call_ops[i].dir = GRPC_CALL_DOWN;
call_ops[i].flags = waiting_children[i]->s.waiting.start_flags;
call_ops[i].done_cb = waiting_children[i]->s.waiting.on_complete;
call_ops[i].user_data =
waiting_children[i]->s.waiting.on_complete_user_data;
call_ops[i].data.start.pollset = waiting_children[i]->s.waiting.pollset;
call_ops[i] = waiting_children[i]->s.waiting_op;
if (!prepare_activate(waiting_children[i]->elem, chand->active_child)) {
waiting_children[i] = NULL;
call_ops[i].done_cb(call_ops[i].user_data, GRPC_OP_ERROR);

@ -60,7 +60,6 @@ typedef struct connected_channel_call_data {
gpr_uint32 max_message_length;
gpr_uint32 incoming_message_length;
gpr_uint8 reading_message;
gpr_uint8 got_metadata_boundary;
gpr_uint8 got_read_close;
gpr_slice_buffer incoming_message;
gpr_uint32 outgoing_buffer_length_estimate;
@ -120,27 +119,20 @@ static void call_op(grpc_call_element *elem, grpc_call_element *from_elem,
GPR_ASSERT(elem->filter == &grpc_connected_channel_filter);
GRPC_CALL_LOG_OP(GPR_INFO, elem, op);
if (op->bind_pollset) {
grpc_transport_add_to_pollset(chand->transport, op->bind_pollset);
}
switch (op->type) {
case GRPC_SEND_METADATA:
grpc_sopb_add_metadata(&calld->outgoing_sopb, op->data.metadata);
grpc_sopb_add_flow_ctl_cb(&calld->outgoing_sopb, op->done_cb,
op->user_data);
break;
case GRPC_SEND_DEADLINE:
grpc_sopb_add_deadline(&calld->outgoing_sopb, op->data.deadline);
grpc_sopb_add_flow_ctl_cb(&calld->outgoing_sopb, op->done_cb,
op->user_data);
break;
case GRPC_SEND_START:
grpc_transport_add_to_pollset(chand->transport, op->data.start.pollset);
grpc_sopb_add_metadata_boundary(&calld->outgoing_sopb);
end_bufferable_op(op, chand, calld, 0);
break;
case GRPC_SEND_MESSAGE:
grpc_sopb_add_begin_message(&calld->outgoing_sopb,
grpc_byte_buffer_length(op->data.message),
op->flags);
/* fall-through */
/* fall-through */
case GRPC_SEND_PREFORMATTED_MESSAGE:
copy_byte_buffer_to_stream_ops(op->data.message, &calld->outgoing_sopb);
calld->outgoing_buffer_length_estimate +=
@ -200,7 +192,6 @@ static void init_call_elem(grpc_call_element *elem,
grpc_sopb_init(&calld->outgoing_sopb);
calld->reading_message = 0;
calld->got_metadata_boundary = 0;
calld->got_read_close = 0;
calld->outgoing_buffer_length_estimate = 0;
calld->max_message_length = chand->max_message_length;
@ -259,9 +250,9 @@ static void destroy_channel_elem(grpc_channel_element *elem) {
}
const grpc_channel_filter grpc_connected_channel_filter = {
call_op, channel_op, sizeof(call_data),
init_call_elem, destroy_call_elem, sizeof(channel_data),
init_channel_elem, destroy_channel_elem, "connected", };
call_op, channel_op, sizeof(call_data), init_call_elem, destroy_call_elem,
sizeof(channel_data), init_channel_elem, destroy_channel_elem, "connected",
};
static gpr_slice alloc_recv_buffer(void *user_data, grpc_transport *transport,
grpc_stream *stream, size_t size_hint) {
@ -307,8 +298,8 @@ static void finish_message(channel_data *chand, call_data *calld) {
call_op.type = GRPC_RECV_MESSAGE;
call_op.done_cb = do_nothing;
/* TODO(ctiller): this could be a lot faster if coded directly */
call_op.data.message = grpc_byte_buffer_create(
calld->incoming_message.slices, calld->incoming_message.count);
call_op.data.message = grpc_byte_buffer_create(calld->incoming_message.slices,
calld->incoming_message.count);
gpr_slice_buffer_reset_and_unref(&calld->incoming_message);
/* disable window updates until we get a request more from above */
@ -320,6 +311,19 @@ static void finish_message(channel_data *chand, call_data *calld) {
grpc_call_next_op(elem, &call_op);
}
static void got_metadata(grpc_call_element *elem,
grpc_metadata_batch metadata) {
grpc_call_op op;
op.type = GRPC_RECV_METADATA;
op.dir = GRPC_CALL_UP;
op.flags = 0;
op.data.metadata = metadata;
op.done_cb = do_nothing;
op.user_data = NULL;
grpc_call_next_op(elem, &op);
}
/* Handle incoming stream ops from the transport, translating them into
call_ops to pass up the call stack */
static void recv_batch(void *user_data, grpc_transport *transport,
@ -339,40 +343,12 @@ static void recv_batch(void *user_data, grpc_transport *transport,
stream_op = ops + i;
switch (stream_op->type) {
case GRPC_OP_FLOW_CTL_CB:
gpr_log(GPR_ERROR,
"should not receive flow control ops from transport");
abort();
stream_op->data.flow_ctl_cb.cb(stream_op->data.flow_ctl_cb.arg, 1);
break;
case GRPC_NO_OP:
break;
case GRPC_OP_METADATA:
call_op.type = GRPC_RECV_METADATA;
call_op.dir = GRPC_CALL_UP;
call_op.flags = 0;
call_op.data.metadata = stream_op->data.metadata;
call_op.done_cb = do_nothing;
call_op.user_data = NULL;
grpc_call_next_op(elem, &call_op);
break;
case GRPC_OP_DEADLINE:
call_op.type = GRPC_RECV_DEADLINE;
call_op.dir = GRPC_CALL_UP;
call_op.flags = 0;
call_op.data.deadline = stream_op->data.deadline;
call_op.done_cb = do_nothing;
call_op.user_data = NULL;
grpc_call_next_op(elem, &call_op);
break;
case GRPC_OP_METADATA_BOUNDARY:
if (!calld->got_metadata_boundary) {
calld->got_metadata_boundary = 1;
call_op.type = GRPC_RECV_END_OF_INITIAL_METADATA;
call_op.dir = GRPC_CALL_UP;
call_op.flags = 0;
call_op.done_cb = do_nothing;
call_op.user_data = NULL;
grpc_call_next_op(elem, &call_op);
}
got_metadata(elem, stream_op->data.metadata);
break;
case GRPC_OP_BEGIN_MESSAGE:
/* can't begin a message when we're still reading a message */
@ -495,7 +471,8 @@ static void transport_closed(void *user_data, grpc_transport *transport) {
const grpc_transport_callbacks connected_channel_transport_callbacks = {
alloc_recv_buffer, accept_stream, recv_batch,
transport_goaway, transport_closed, };
transport_goaway, transport_closed,
};
grpc_transport_setup_result grpc_connected_channel_bind_transport(
grpc_channel_stack *channel_stack, grpc_transport *transport) {

@ -35,7 +35,10 @@
#include <grpc/support/log.h>
typedef struct call_data {
int sent_headers;
grpc_linked_mdelem method;
grpc_linked_mdelem scheme;
grpc_linked_mdelem te_trailers;
grpc_linked_mdelem content_type;
} call_data;
typedef struct channel_data {
@ -49,6 +52,18 @@ typedef struct channel_data {
/* used to silence 'variable not used' warnings */
static void ignore_unused(void *ignored) {}
static grpc_mdelem *client_filter(void *user_data, grpc_mdelem *md) {
grpc_call_element *elem = user_data;
channel_data *channeld = elem->channel_data;
if (md == channeld->status) {
return NULL;
} else if (md->key == channeld->status->key) {
grpc_call_element_send_cancel(elem);
return NULL;
}
return md;
}
/* Called either:
- in response to an API call (or similar) from above, to send something
- a network event (or similar) from below, to receive something
@ -61,42 +76,23 @@ static void call_op(grpc_call_element *elem, grpc_call_element *from_elem,
channel_data *channeld = elem->channel_data;
GRPC_CALL_LOG_OP(GPR_INFO, elem, op);
ignore_unused(calld);
switch (op->type) {
case GRPC_SEND_METADATA:
if (!calld->sent_headers) {
/* Send : prefixed headers, which have to be before any application
* layer headers. */
calld->sent_headers = 1;
grpc_call_element_send_metadata(elem, grpc_mdelem_ref(channeld->method));
grpc_call_element_send_metadata(elem, grpc_mdelem_ref(channeld->scheme));
}
grpc_call_next_op(elem, op);
break;
case GRPC_SEND_START:
if (!calld->sent_headers) {
/* Send : prefixed headers, if we haven't already */
calld->sent_headers = 1;
grpc_call_element_send_metadata(elem, grpc_mdelem_ref(channeld->method));
grpc_call_element_send_metadata(elem, grpc_mdelem_ref(channeld->scheme));
}
/* Send non : prefixed headers */
grpc_call_element_send_metadata(elem, grpc_mdelem_ref(channeld->te_trailers));
grpc_call_element_send_metadata(elem, grpc_mdelem_ref(channeld->content_type));
/* Send : prefixed headers, which have to be before any application
* layer headers. */
grpc_metadata_batch_add_head(&op->data.metadata, &calld->method,
grpc_mdelem_ref(channeld->method));
grpc_metadata_batch_add_head(&op->data.metadata, &calld->scheme,
grpc_mdelem_ref(channeld->scheme));
grpc_metadata_batch_add_tail(&op->data.metadata, &calld->te_trailers,
grpc_mdelem_ref(channeld->te_trailers));
grpc_metadata_batch_add_tail(&op->data.metadata, &calld->content_type,
grpc_mdelem_ref(channeld->content_type));
grpc_call_next_op(elem, op);
break;
case GRPC_RECV_METADATA:
if (op->data.metadata == channeld->status) {
grpc_mdelem_unref(op->data.metadata);
op->done_cb(op->user_data, GRPC_OP_OK);
} else if (op->data.metadata->key == channeld->status->key) {
grpc_mdelem_unref(op->data.metadata);
op->done_cb(op->user_data, GRPC_OP_OK);
grpc_call_element_send_cancel(elem);
} else {
grpc_call_next_op(elem, op);
}
grpc_metadata_batch_filter(&op->data.metadata, client_filter, elem);
grpc_call_next_op(elem, op);
break;
default:
/* pass control up or down the stack depending on op->dir */
@ -124,16 +120,7 @@ static void channel_op(grpc_channel_element *elem,
/* Constructor for call_data */
static void init_call_elem(grpc_call_element *elem,
const void *server_transport_data) {
/* grab pointers to our data from the call element */
call_data *calld = elem->call_data;
channel_data *channeld = elem->channel_data;
ignore_unused(channeld);
/* initialize members */
calld->sent_headers = 0;
}
const void *server_transport_data) {}
/* Destructor for call_data */
static void destroy_call_elem(grpc_call_element *elem) {
@ -194,6 +181,6 @@ static void destroy_channel_elem(grpc_channel_element *elem) {
}
const grpc_channel_filter grpc_http_client_filter = {
call_op, channel_op, sizeof(call_data),
init_call_elem, destroy_call_elem, sizeof(channel_data),
init_channel_elem, destroy_channel_elem, "http-client"};
call_op, channel_op, sizeof(call_data), init_call_elem, destroy_call_elem,
sizeof(channel_data), init_channel_elem, destroy_channel_elem,
"http-client"};

@ -38,8 +38,6 @@
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
typedef enum { NOT_RECEIVED, POST, GET } known_method_type;
typedef struct {
grpc_mdelem *path;
grpc_mdelem *content_type;
@ -47,16 +45,17 @@ typedef struct {
} gettable;
typedef struct call_data {
known_method_type seen_method;
gpr_uint8 got_initial_metadata;
gpr_uint8 seen_path;
gpr_uint8 seen_post;
gpr_uint8 sent_status;
gpr_uint8 seen_scheme;
gpr_uint8 seen_te_trailers;
grpc_mdelem *path;
grpc_linked_mdelem status;
} call_data;
typedef struct channel_data {
grpc_mdelem *te_trailers;
grpc_mdelem *method_get;
grpc_mdelem *method_post;
grpc_mdelem *http_scheme;
grpc_mdelem *https_scheme;
@ -78,38 +77,70 @@ typedef struct channel_data {
/* used to silence 'variable not used' warnings */
static void ignore_unused(void *ignored) {}
/* Handle 'GET': not technically grpc, so probably a web browser hitting
us */
static void payload_done(void *elem, grpc_op_error error) {
if (error == GRPC_OP_OK) {
grpc_call_element_send_finish(elem);
}
}
static void handle_get(grpc_call_element *elem) {
static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) {
grpc_call_element *elem = user_data;
channel_data *channeld = elem->channel_data;
call_data *calld = elem->call_data;
grpc_call_op op;
size_t i;
for (i = 0; i < channeld->gettable_count; i++) {
if (channeld->gettables[i].path == calld->path) {
grpc_call_element_send_metadata(elem,
grpc_mdelem_ref(channeld->status_ok));
grpc_call_element_send_metadata(
elem, grpc_mdelem_ref(channeld->gettables[i].content_type));
op.type = GRPC_SEND_PREFORMATTED_MESSAGE;
op.dir = GRPC_CALL_DOWN;
op.flags = 0;
op.data.message = channeld->gettables[i].content;
op.done_cb = payload_done;
op.user_data = elem;
grpc_call_next_op(elem, &op);
/* Check if it is one of the headers we care about. */
if (md == channeld->te_trailers || md == channeld->method_post ||
md == channeld->http_scheme || md == channeld->https_scheme ||
md == channeld->grpc_scheme || md == channeld->content_type) {
/* swallow it */
if (md == channeld->method_post) {
calld->seen_post = 1;
} else if (md->key == channeld->http_scheme->key) {
calld->seen_scheme = 1;
} else if (md == channeld->te_trailers) {
calld->seen_te_trailers = 1;
}
/* TODO(klempner): Track that we've seen all the headers we should
require */
return NULL;
} else if (md->key == channeld->content_type->key) {
if (strncmp(grpc_mdstr_as_c_string(md->value), "application/grpc+", 17) ==
0) {
/* Although the C implementation doesn't (currently) generate them,
any custom +-suffix is explicitly valid. */
/* TODO(klempner): We should consider preallocating common values such
as +proto or +json, or at least stashing them if we see them. */
/* TODO(klempner): Should we be surfacing this to application code? */
} else {
/* TODO(klempner): We're currently allowing this, but we shouldn't
see it without a proxy so log for now. */
gpr_log(GPR_INFO, "Unexpected content-type %s",
channeld->content_type->key);
}
return NULL;
} else if (md->key == channeld->te_trailers->key ||
md->key == channeld->method_post->key ||
md->key == channeld->http_scheme->key ||
md->key == channeld->content_type->key) {
gpr_log(GPR_ERROR, "Invalid %s: header: '%s'",
grpc_mdstr_as_c_string(md->key), grpc_mdstr_as_c_string(md->value));
/* swallow it and error everything out. */
/* TODO(klempner): We ought to generate more descriptive error messages
on the wire here. */
grpc_call_element_send_cancel(elem);
return NULL;
} else if (md->key == channeld->path_key) {
if (calld->seen_path) {
gpr_log(GPR_ERROR, "Received :path twice");
return NULL;
}
calld->seen_path = 1;
return md;
} else if (md->key == channeld->host_key) {
/* translate host to :authority since :authority may be
omitted */
grpc_mdelem *authority = grpc_mdelem_from_metadata_strings(
channeld->mdctx, grpc_mdstr_ref(channeld->authority_key),
grpc_mdstr_ref(md->value));
grpc_mdelem_unref(md);
return authority;
} else {
return md;
}
grpc_call_element_send_metadata(elem,
grpc_mdelem_ref(channeld->status_not_found));
grpc_call_element_send_finish(elem);
}
/* Called either:
@ -126,115 +157,44 @@ static void call_op(grpc_call_element *elem, grpc_call_element *from_elem,
switch (op->type) {
case GRPC_RECV_METADATA:
/* Check if it is one of the headers we care about. */
if (op->data.metadata == channeld->te_trailers ||
op->data.metadata == channeld->method_get ||
op->data.metadata == channeld->method_post ||
op->data.metadata == channeld->http_scheme ||
op->data.metadata == channeld->https_scheme ||
op->data.metadata == channeld->grpc_scheme ||
op->data.metadata == channeld->content_type) {
/* swallow it */
if (op->data.metadata == channeld->method_get) {
calld->seen_method = GET;
} else if (op->data.metadata == channeld->method_post) {
calld->seen_method = POST;
} else if (op->data.metadata->key == channeld->http_scheme->key) {
calld->seen_scheme = 1;
} else if (op->data.metadata == channeld->te_trailers) {
calld->seen_te_trailers = 1;
}
/* TODO(klempner): Track that we've seen all the headers we should
require */
grpc_mdelem_unref(op->data.metadata);
op->done_cb(op->user_data, GRPC_OP_OK);
} else if (op->data.metadata->key == channeld->content_type->key) {
if (strncmp(grpc_mdstr_as_c_string(op->data.metadata->value),
"application/grpc+", 17) == 0) {
/* Although the C implementation doesn't (currently) generate them,
any
custom +-suffix is explicitly valid. */
/* TODO(klempner): We should consider preallocating common values such
as +proto or +json, or at least stashing them if we see them. */
/* TODO(klempner): Should we be surfacing this to application code? */
grpc_metadata_batch_filter(&op->data.metadata, server_filter, elem);
if (!calld->got_initial_metadata) {
calld->got_initial_metadata = 1;
/* Have we seen the required http2 transport headers?
(:method, :scheme, content-type, with :path and :authority covered
at the channel level right now) */
if (calld->seen_post && calld->seen_scheme && calld->seen_te_trailers &&
calld->seen_path) {
grpc_call_next_op(elem, op);
} else {
/* TODO(klempner): We're currently allowing this, but we shouldn't
see it without a proxy so log for now. */
gpr_log(GPR_INFO, "Unexpected content-type %s",
channeld->content_type->key);
}
grpc_mdelem_unref(op->data.metadata);
op->done_cb(op->user_data, GRPC_OP_OK);
} else if (op->data.metadata->key == channeld->te_trailers->key ||
op->data.metadata->key == channeld->method_post->key ||
op->data.metadata->key == channeld->http_scheme->key ||
op->data.metadata->key == channeld->content_type->key) {
gpr_log(GPR_ERROR, "Invalid %s: header: '%s'",
grpc_mdstr_as_c_string(op->data.metadata->key),
grpc_mdstr_as_c_string(op->data.metadata->value));
/* swallow it and error everything out. */
/* TODO(klempner): We ought to generate more descriptive error messages
on the wire here. */
grpc_mdelem_unref(op->data.metadata);
op->done_cb(op->user_data, GRPC_OP_OK);
grpc_call_element_send_cancel(elem);
} else if (op->data.metadata->key == channeld->path_key) {
if (calld->path != NULL) {
gpr_log(GPR_ERROR, "Received :path twice");
grpc_mdelem_unref(calld->path);
if (!calld->seen_path) {
gpr_log(GPR_ERROR, "Missing :path header");
}
if (!calld->seen_post) {
gpr_log(GPR_ERROR, "Missing :method header");
}
if (!calld->seen_scheme) {
gpr_log(GPR_ERROR, "Missing :scheme header");
}
if (!calld->seen_te_trailers) {
gpr_log(GPR_ERROR, "Missing te trailers header");
}
/* Error this call out */
grpc_metadata_batch_destroy(&op->data.metadata);
op->done_cb(op->user_data, GRPC_OP_OK);
grpc_call_element_send_cancel(elem);
}
calld->path = op->data.metadata;
op->done_cb(op->user_data, GRPC_OP_OK);
} else if (op->data.metadata->key == channeld->host_key) {
/* translate host to :authority since :authority may be
omitted */
grpc_mdelem *authority = grpc_mdelem_from_metadata_strings(
channeld->mdctx, grpc_mdstr_ref(channeld->authority_key),
grpc_mdstr_ref(op->data.metadata->value));
grpc_mdelem_unref(op->data.metadata);
op->data.metadata = authority;
/* pass the event up */
grpc_call_next_op(elem, op);
} else {
/* pass the event up */
grpc_call_next_op(elem, op);
}
break;
case GRPC_RECV_END_OF_INITIAL_METADATA:
/* Have we seen the required http2 transport headers?
(:method, :scheme, content-type, with :path and :authority covered
at the channel level right now) */
if (calld->seen_method == POST && calld->seen_scheme &&
calld->seen_te_trailers && calld->path) {
grpc_call_element_recv_metadata(elem, calld->path);
calld->path = NULL;
grpc_call_next_op(elem, op);
} else if (calld->seen_method == GET) {
handle_get(elem);
} else {
if (calld->seen_method == NOT_RECEIVED) {
gpr_log(GPR_ERROR, "Missing :method header");
}
if (!calld->seen_scheme) {
gpr_log(GPR_ERROR, "Missing :scheme header");
}
if (!calld->seen_te_trailers) {
gpr_log(GPR_ERROR, "Missing te trailers header");
}
/* Error this call out */
op->done_cb(op->user_data, GRPC_OP_OK);
grpc_call_element_send_cancel(elem);
}
break;
case GRPC_SEND_START:
case GRPC_SEND_METADATA:
/* If we haven't sent status 200 yet, we need to so so because it needs to
come before any non : prefixed metadata. */
if (!calld->sent_status) {
calld->sent_status = 1;
/* status is reffed by grpc_call_element_send_metadata */
grpc_call_element_send_metadata(elem,
grpc_mdelem_ref(channeld->status_ok));
grpc_metadata_batch_add_head(&op->data.metadata, &calld->status,
grpc_mdelem_ref(channeld->status_ok));
}
grpc_call_next_op(elem, op);
break;
@ -272,25 +232,11 @@ static void init_call_elem(grpc_call_element *elem,
ignore_unused(channeld);
/* initialize members */
calld->path = NULL;
calld->sent_status = 0;
calld->seen_scheme = 0;
calld->seen_method = NOT_RECEIVED;
calld->seen_te_trailers = 0;
memset(calld, 0, sizeof(*calld));
}
/* Destructor for call_data */
static void destroy_call_elem(grpc_call_element *elem) {
/* grab pointers to our data from the call element */
call_data *calld = elem->call_data;
channel_data *channeld = elem->channel_data;
ignore_unused(channeld);
if (calld->path) {
grpc_mdelem_unref(calld->path);
}
}
static void destroy_call_elem(grpc_call_element *elem) {}
/* Constructor for channel_data */
static void init_channel_elem(grpc_channel_element *elem,
@ -314,7 +260,6 @@ static void init_channel_elem(grpc_channel_element *elem,
channeld->status_not_found =
grpc_mdelem_from_strings(mdctx, ":status", "404");
channeld->method_post = grpc_mdelem_from_strings(mdctx, ":method", "POST");
channeld->method_get = grpc_mdelem_from_strings(mdctx, ":method", "GET");
channeld->http_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "http");
channeld->https_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "https");
channeld->grpc_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "grpc");
@ -369,7 +314,6 @@ static void destroy_channel_elem(grpc_channel_element *elem) {
grpc_mdelem_unref(channeld->status_ok);
grpc_mdelem_unref(channeld->status_not_found);
grpc_mdelem_unref(channeld->method_post);
grpc_mdelem_unref(channeld->method_get);
grpc_mdelem_unref(channeld->http_scheme);
grpc_mdelem_unref(channeld->https_scheme);
grpc_mdelem_unref(channeld->grpc_scheme);

@ -1,149 +0,0 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "src/core/channel/metadata_buffer.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/useful.h>
#include <string.h>
#define INITIAL_ELEM_CAP 8
/* One queued call; we track offsets to string data in a shared buffer to
reduce allocations. See grpc_metadata_buffer_impl for the memory use
strategy */
typedef struct {
grpc_mdelem *md;
void (*cb)(void *user_data, grpc_op_error error);
void *user_data;
gpr_uint32 flags;
} qelem;
/* Memory layout:
grpc_metadata_buffer_impl
followed by an array of qelem */
struct grpc_metadata_buffer_impl {
/* number of elements in q */
size_t elems;
/* capacity of q */
size_t elem_cap;
};
#define ELEMS(buffer) ((qelem *)((buffer) + 1))
void grpc_metadata_buffer_init(grpc_metadata_buffer *buffer) {
/* start buffer as NULL, indicating no elements */
*buffer = NULL;
}
void grpc_metadata_buffer_destroy(grpc_metadata_buffer *buffer,
grpc_op_error error) {
size_t i;
qelem *qe;
if (*buffer) {
for (i = 0; i < (*buffer)->elems; i++) {
qe = &ELEMS(*buffer)[i];
grpc_mdelem_unref(qe->md);
qe->cb(qe->user_data, error);
}
gpr_free(*buffer);
}
}
void grpc_metadata_buffer_queue(grpc_metadata_buffer *buffer,
grpc_call_op *op) {
grpc_metadata_buffer_impl *impl = *buffer;
qelem *qe;
size_t bytes;
GPR_ASSERT(op->type == GRPC_SEND_METADATA || op->type == GRPC_RECV_METADATA);
if (!impl) {
/* this is the first element: allocate enough space to hold the
header object and the initial element capacity of qelems */
bytes =
sizeof(grpc_metadata_buffer_impl) + INITIAL_ELEM_CAP * sizeof(qelem);
impl = gpr_malloc(bytes);
/* initialize the header object */
impl->elems = 0;
impl->elem_cap = INITIAL_ELEM_CAP;
} else if (impl->elems == impl->elem_cap) {
/* more qelems than what we can deal with: grow by doubling size */
impl->elem_cap *= 2;
bytes = sizeof(grpc_metadata_buffer_impl) + impl->elem_cap * sizeof(qelem);
impl = gpr_realloc(impl, bytes);
}
/* append an element to the queue */
qe = &ELEMS(impl)[impl->elems];
impl->elems++;
qe->md = op->data.metadata;
qe->cb = op->done_cb;
qe->user_data = op->user_data;
qe->flags = op->flags;
/* header object may have changed location: store it back */
*buffer = impl;
}
void grpc_metadata_buffer_flush(grpc_metadata_buffer *buffer,
grpc_call_element *elem) {
grpc_metadata_buffer_impl *impl = *buffer;
grpc_call_op op;
qelem *qe;
size_t i;
if (!impl) {
/* nothing to send */
return;
}
/* construct call_op's, and push them down the stack */
op.type = GRPC_SEND_METADATA;
op.dir = GRPC_CALL_DOWN;
for (i = 0; i < impl->elems; i++) {
qe = &ELEMS(impl)[i];
op.done_cb = qe->cb;
op.user_data = qe->user_data;
op.flags = qe->flags;
op.data.metadata = qe->md;
grpc_call_next_op(elem, &op);
}
/* free data structures and reset to NULL: we can only flush once */
gpr_free(impl);
*buffer = NULL;
}

@ -1,70 +0,0 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef GRPC_INTERNAL_CORE_CHANNEL_METADATA_BUFFER_H
#define GRPC_INTERNAL_CORE_CHANNEL_METADATA_BUFFER_H
#include "src/core/channel/channel_stack.h"
/* Utility code to buffer GRPC_SEND_METADATA calls and pass them down the stack
all at once at some otherwise-determined time. Useful for implementing
filters that want to queue metadata until a START event chooses some
underlying filter stack to send an rpc on. */
/* Clients should declare a member of grpc_metadata_buffer. This may at some
point become a typedef for a struct, but for now a pointer suffices */
typedef struct grpc_metadata_buffer_impl grpc_metadata_buffer_impl;
typedef grpc_metadata_buffer_impl *grpc_metadata_buffer;
/* Initializes the metadata buffer. Allocates no memory. */
void grpc_metadata_buffer_init(grpc_metadata_buffer *buffer);
/* Destroy the metadata buffer. */
void grpc_metadata_buffer_destroy(grpc_metadata_buffer *buffer,
grpc_op_error error);
/* Append a call to the end of a metadata buffer: may allocate memory */
void grpc_metadata_buffer_queue(grpc_metadata_buffer *buffer, grpc_call_op *op);
/* Flush all queued operations from the metadata buffer to the element below
self */
void grpc_metadata_buffer_flush(grpc_metadata_buffer *buffer,
grpc_call_element *self);
/* Count the number of queued elements in the buffer. */
size_t grpc_metadata_buffer_count(const grpc_metadata_buffer *buffer);
/* Extract elements as a grpc_metadata*, for presentation to applications.
The returned buffer must be freed with
grpc_metadata_buffer_cleanup_elements.
Clears the metadata buffer (this is a one-shot operation) */
grpc_metadata *grpc_metadata_buffer_extract_elements(
grpc_metadata_buffer *buffer);
void grpc_metadata_buffer_cleanup_elements(void *elements, grpc_op_error error);
#endif /* GRPC_INTERNAL_CORE_CHANNEL_METADATA_BUFFER_H */

@ -40,9 +40,8 @@
#include "src/core/iomgr/resolve_address.h"
#include "src/core/iomgr/tcp_client.h"
#include "src/core/httpcli/format_request.h"
#include "src/core/httpcli/httpcli_security_context.h"
#include "src/core/httpcli/httpcli_security_connector.h"
#include "src/core/httpcli/parser.h"
#include "src/core/security/security_context.h"
#include "src/core/security/secure_transport_setup.h"
#include "src/core/support/string.h"
#include <grpc/support/alloc.h>
@ -180,7 +179,7 @@ static void on_connected(void *arg, grpc_endpoint *tcp) {
}
req->ep = tcp;
if (req->use_ssl) {
grpc_channel_security_context *ctx = NULL;
grpc_channel_security_connector *sc = NULL;
const unsigned char *pem_root_certs = NULL;
size_t pem_root_certs_size = grpc_get_default_ssl_roots(&pem_root_certs);
if (pem_root_certs == NULL || pem_root_certs_size == 0) {
@ -188,12 +187,12 @@ static void on_connected(void *arg, grpc_endpoint *tcp) {
finish(req, 0);
return;
}
GPR_ASSERT(grpc_httpcli_ssl_channel_security_context_create(
pem_root_certs, pem_root_certs_size, req->host, &ctx) ==
GPR_ASSERT(grpc_httpcli_ssl_channel_security_connector_create(
pem_root_certs, pem_root_certs_size, req->host, &sc) ==
GRPC_SECURITY_OK);
grpc_setup_secure_transport(&ctx->base, tcp, on_secure_transport_setup_done,
grpc_setup_secure_transport(&sc->base, tcp, on_secure_transport_setup_done,
req);
grpc_security_context_unref(&ctx->base);
grpc_security_connector_unref(&sc->base);
} else {
start_write(req);
}

@ -31,7 +31,7 @@
*
*/
#include "src/core/httpcli/httpcli_security_context.h"
#include "src/core/httpcli/httpcli_security_connector.h"
#include <string.h>
@ -42,25 +42,25 @@
#include "src/core/tsi/ssl_transport_security.h"
typedef struct {
grpc_channel_security_context base;
grpc_channel_security_connector base;
tsi_ssl_handshaker_factory *handshaker_factory;
char *secure_peer_name;
} grpc_httpcli_ssl_channel_security_context;
} grpc_httpcli_ssl_channel_security_connector;
static void httpcli_ssl_destroy(grpc_security_context *ctx) {
grpc_httpcli_ssl_channel_security_context *c =
(grpc_httpcli_ssl_channel_security_context *)ctx;
static void httpcli_ssl_destroy(grpc_security_connector *sc) {
grpc_httpcli_ssl_channel_security_connector *c =
(grpc_httpcli_ssl_channel_security_connector *)sc;
if (c->handshaker_factory != NULL) {
tsi_ssl_handshaker_factory_destroy(c->handshaker_factory);
}
if (c->secure_peer_name != NULL) gpr_free(c->secure_peer_name);
gpr_free(ctx);
gpr_free(sc);
}
static grpc_security_status httpcli_ssl_create_handshaker(
grpc_security_context *ctx, tsi_handshaker **handshaker) {
grpc_httpcli_ssl_channel_security_context *c =
(grpc_httpcli_ssl_channel_security_context *)ctx;
grpc_security_connector *sc, tsi_handshaker **handshaker) {
grpc_httpcli_ssl_channel_security_connector *c =
(grpc_httpcli_ssl_channel_security_connector *)sc;
tsi_result result = TSI_OK;
if (c->handshaker_factory == NULL) return GRPC_SECURITY_ERROR;
result = tsi_ssl_handshaker_factory_create_handshaker(
@ -73,12 +73,12 @@ static grpc_security_status httpcli_ssl_create_handshaker(
return GRPC_SECURITY_OK;
}
static grpc_security_status httpcli_ssl_check_peer(grpc_security_context *ctx,
static grpc_security_status httpcli_ssl_check_peer(grpc_security_connector *sc,
tsi_peer peer,
grpc_security_check_cb cb,
void *user_data) {
grpc_httpcli_ssl_channel_security_context *c =
(grpc_httpcli_ssl_channel_security_context *)ctx;
grpc_httpcli_ssl_channel_security_connector *c =
(grpc_httpcli_ssl_channel_security_connector *)sc;
grpc_security_status status = GRPC_SECURITY_OK;
/* Check the peer name. */
@ -92,14 +92,14 @@ static grpc_security_status httpcli_ssl_check_peer(grpc_security_context *ctx,
return status;
}
static grpc_security_context_vtable httpcli_ssl_vtable = {
static grpc_security_connector_vtable httpcli_ssl_vtable = {
httpcli_ssl_destroy, httpcli_ssl_create_handshaker, httpcli_ssl_check_peer};
grpc_security_status grpc_httpcli_ssl_channel_security_context_create(
grpc_security_status grpc_httpcli_ssl_channel_security_connector_create(
const unsigned char *pem_root_certs, size_t pem_root_certs_size,
const char *secure_peer_name, grpc_channel_security_context **ctx) {
const char *secure_peer_name, grpc_channel_security_connector **sc) {
tsi_result result = TSI_OK;
grpc_httpcli_ssl_channel_security_context *c;
grpc_httpcli_ssl_channel_security_connector *c;
if (secure_peer_name != NULL && pem_root_certs == NULL) {
gpr_log(GPR_ERROR,
@ -107,8 +107,8 @@ grpc_security_status grpc_httpcli_ssl_channel_security_context_create(
return GRPC_SECURITY_ERROR;
}
c = gpr_malloc(sizeof(grpc_httpcli_ssl_channel_security_context));
memset(c, 0, sizeof(grpc_httpcli_ssl_channel_security_context));
c = gpr_malloc(sizeof(grpc_httpcli_ssl_channel_security_connector));
memset(c, 0, sizeof(grpc_httpcli_ssl_channel_security_connector));
gpr_ref_init(&c->base.base.refcount, 1);
c->base.base.is_client_side = 1;
@ -123,9 +123,9 @@ grpc_security_status grpc_httpcli_ssl_channel_security_context_create(
gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.",
tsi_result_to_string(result));
httpcli_ssl_destroy(&c->base.base);
*ctx = NULL;
*sc = NULL;
return GRPC_SECURITY_ERROR;
}
*ctx = &c->base;
*sc = &c->base;
return GRPC_SECURITY_OK;
}

@ -31,13 +31,13 @@
*
*/
#ifndef GRPC_INTERNAL_CORE_HTTPCLI_HTTPCLI_SECURITY_CONTEXT_H
#define GRPC_INTERNAL_CORE_HTTPCLI_HTTPCLI_SECURITY_CONTEXT_H
#ifndef GRPC_INTERNAL_CORE_HTTPCLI_HTTPCLI_SECURITY_CONNECTOR_H
#define GRPC_INTERNAL_CORE_HTTPCLI_HTTPCLI_SECURITY_CONNECTOR_H
#include "src/core/security/security_context.h"
#include "src/core/security/security_connector.h"
grpc_security_status grpc_httpcli_ssl_channel_security_context_create(
grpc_security_status grpc_httpcli_ssl_channel_security_connector_create(
const unsigned char *pem_root_certs, size_t pem_root_certs_size,
const char *secure_peer_name, grpc_channel_security_context **ctx);
const char *secure_peer_name, grpc_channel_security_connector **sc);
#endif /* GRPC_INTERNAL_CORE_HTTPCLI_HTTPCLI_SECURITY_CONTEXT_H */
#endif /* GRPC_INTERNAL_CORE_HTTPCLI_HTTPCLI_SECURITY_CONNECTOR_H */

@ -0,0 +1,85 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <grpc/support/port_platform.h>
#ifdef GPR_WINSOCK_SOCKET
#include "src/core/iomgr/sockaddr_utils.h"
#include "src/core/iomgr/endpoint_pair.h"
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include "src/core/iomgr/tcp_windows.h"
#include "src/core/iomgr/socket_windows.h"
#include <grpc/support/log.h>
static void create_sockets(SOCKET sv[2]) {
SOCKET svr_sock = INVALID_SOCKET;
SOCKET lst_sock = INVALID_SOCKET;
SOCKET cli_sock = INVALID_SOCKET;
SOCKADDR_IN addr;
int addr_len;
lst_sock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
GPR_ASSERT(lst_sock != INVALID_SOCKET);
memset(&addr, 0, sizeof(addr));
GPR_ASSERT(bind(lst_sock, (struct sockaddr*)&addr, sizeof(addr)) != SOCKET_ERROR);
GPR_ASSERT(listen(lst_sock, SOMAXCONN) != SOCKET_ERROR);
GPR_ASSERT(getsockname(lst_sock, (struct sockaddr*)&addr, &addr_len) != SOCKET_ERROR);
cli_sock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
GPR_ASSERT(cli_sock != INVALID_SOCKET);
GPR_ASSERT(WSAConnect(cli_sock, (struct sockaddr*)&addr, addr_len, NULL, NULL, NULL, NULL) == 0);
svr_sock = accept(lst_sock, (struct sockaddr*)&addr, &addr_len);
GPR_ASSERT(svr_sock != INVALID_SOCKET);
closesocket(lst_sock);
sv[1] = cli_sock;
sv[0] = svr_sock;
}
grpc_endpoint_pair grpc_iomgr_create_endpoint_pair(size_t read_slice_size) {
SOCKET sv[2];
grpc_endpoint_pair p;
create_sockets(sv);
p.client = grpc_tcp_create(grpc_winsocket_create(sv[1]));
p.server = grpc_tcp_create(grpc_winsocket_create(sv[0]));
return p;
}
#endif

@ -78,7 +78,6 @@ static void do_iocp_work() {
if (overlapped == &g_iocp_custom_overlap) {
if (completion_key == (ULONG_PTR) &g_iocp_kick_token) {
/* We were awoken from a kick. */
gpr_log(GPR_DEBUG, "do_iocp_work - got a kick");
return;
}
gpr_log(GPR_ERROR, "Unknown custom completion key.");
@ -87,10 +86,8 @@ static void do_iocp_work() {
socket = (grpc_winsocket*) completion_key;
if (overlapped == &socket->write_info.overlapped) {
gpr_log(GPR_DEBUG, "do_iocp_work - got write packet");
info = &socket->write_info;
} else if (overlapped == &socket->read_info.overlapped) {
gpr_log(GPR_DEBUG, "do_iocp_work - got read packet");
info = &socket->read_info;
} else {
gpr_log(GPR_ERROR, "Unknown IOCP operation");
@ -98,8 +95,6 @@ static void do_iocp_work() {
}
success = WSAGetOverlappedResult(socket->socket, &info->overlapped, &bytes,
FALSE, &flags);
gpr_log(GPR_DEBUG, "bytes: %u, flags: %u - op %s %s", bytes, flags,
success ? "succeeded" : "failed", socket->orphan ? "orphan" : "");
if (socket->orphan) {
grpc_winsocket_destroy(socket);
gpr_atm_full_fetch_add(&g_orphans, -1);
@ -189,11 +184,9 @@ static void socket_notify_on_iocp(grpc_winsocket *socket,
if (info->has_pending_iocp) {
run_now = 1;
info->has_pending_iocp = 0;
gpr_log(GPR_DEBUG, "socket_notify_on_iocp - runs now");
} else {
info->cb = cb;
info->opaque = opaque;
gpr_log(GPR_DEBUG, "socket_notify_on_iocp - queued");
}
gpr_mu_unlock(&socket->state_mu);
if (run_now) cb(opaque, 1);
@ -201,13 +194,11 @@ static void socket_notify_on_iocp(grpc_winsocket *socket,
void grpc_socket_notify_on_write(grpc_winsocket *socket,
void(*cb)(void *, int), void *opaque) {
gpr_log(GPR_DEBUG, "grpc_socket_notify_on_write");
socket_notify_on_iocp(socket, cb, opaque, &socket->write_info);
}
void grpc_socket_notify_on_read(grpc_winsocket *socket,
void(*cb)(void *, int), void *opaque) {
gpr_log(GPR_DEBUG, "grpc_socket_notify_on_read");
socket_notify_on_iocp(socket, cb, opaque, &socket->read_info);
}

@ -107,18 +107,14 @@ grpc_resolved_addresses *grpc_blocking_resolve_address(
i++;
}
/* Temporary logging, to help identify flakiness in dualstack_socket_test. */
{
const gpr_timespec delay = gpr_time_sub(gpr_now(), start_time);
const int delay_ms =
delay.tv_sec * GPR_MS_PER_SEC + delay.tv_nsec / GPR_NS_PER_MS;
gpr_log(GPR_INFO, "logspam: getaddrinfo(%s, %s) resolved %d addrs in %dms:",
host, port, addrs->naddrs, delay_ms);
for (i = 0; i < addrs->naddrs; i++) {
char *buf;
grpc_sockaddr_to_string(&buf, (struct sockaddr *)&addrs->addrs[i].addr,
0);
gpr_log(GPR_INFO, "logspam: [%d] %s", i, buf);
gpr_free(buf);
}
}

@ -46,7 +46,6 @@
grpc_winsocket *grpc_winsocket_create(SOCKET socket) {
grpc_winsocket *r = gpr_malloc(sizeof(grpc_winsocket));
gpr_log(GPR_DEBUG, "grpc_winsocket_create");
memset(r, 0, sizeof(grpc_winsocket));
r->socket = socket;
gpr_mu_init(&r->state_mu);
@ -61,13 +60,11 @@ static void shutdown_op(grpc_winsocket_callback_info *info) {
}
void grpc_winsocket_shutdown(grpc_winsocket *socket) {
gpr_log(GPR_DEBUG, "grpc_winsocket_shutdown");
shutdown_op(&socket->read_info);
shutdown_op(&socket->write_info);
}
void grpc_winsocket_orphan(grpc_winsocket *socket) {
gpr_log(GPR_DEBUG, "grpc_winsocket_orphan");
grpc_iocp_socket_orphan(socket);
socket->orphan = 1;
grpc_iomgr_unref();

@ -102,7 +102,6 @@ static void on_connect(void *acp, int success) {
gpr_free(utf8_message);
goto finish;
} else {
gpr_log(GPR_DEBUG, "on_connect: connection established");
ep = grpc_tcp_create(ac->socket);
goto finish;
}
@ -179,9 +178,7 @@ void grpc_tcp_client_connect(void(*cb)(void *arg, grpc_endpoint *tcp),
info = &socket->write_info;
success = ConnectEx(sock, addr, addr_len, NULL, 0, NULL, &info->overlapped);
if (success) {
gpr_log(GPR_DEBUG, "connected immediately - but we still go to sleep");
} else {
if (!success) {
int error = WSAGetLastError();
if (error != ERROR_IO_PENDING) {
message = "ConnectEx failed: %s";
@ -189,7 +186,6 @@ void grpc_tcp_client_connect(void(*cb)(void *arg, grpc_endpoint *tcp),
}
}
gpr_log(GPR_DEBUG, "grpc_tcp_client_connect: connection pending");
ac = gpr_malloc(sizeof(async_connect));
ac->cb = cb;
ac->cb_arg = arg;

@ -46,6 +46,7 @@
#include "src/core/support/string.h"
#include "src/core/debug/trace.h"
#include "src/core/profiling/timers.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/slice.h>
@ -326,6 +327,7 @@ static void grpc_tcp_handle_read(void *arg /* grpc_tcp */, int success) {
gpr_slice *final_slices;
size_t final_nslices;
GRPC_TIMER_MARK(HANDLE_READ_BEGIN, 0);
slice_state_init(&read_state, static_read_slices, INLINE_SLICE_BUFFER_SIZE,
0);
@ -348,9 +350,11 @@ static void grpc_tcp_handle_read(void *arg /* grpc_tcp */, int success) {
msg.msg_controllen = 0;
msg.msg_flags = 0;
GRPC_TIMER_MARK(RECVMSG_BEGIN, 0);
do {
read_bytes = recvmsg(tcp->fd, &msg, 0);
} while (read_bytes < 0 && errno == EINTR);
GRPC_TIMER_MARK(RECVMSG_END, 0);
if (read_bytes < allocated_bytes) {
/* TODO(klempner): Consider a second read first, in hopes of getting a
@ -402,6 +406,7 @@ static void grpc_tcp_handle_read(void *arg /* grpc_tcp */, int success) {
++iov_size;
}
}
GRPC_TIMER_MARK(HANDLE_READ_END, 0);
}
static void grpc_tcp_notify_on_read(grpc_endpoint *ep, grpc_endpoint_read_cb cb,
@ -433,10 +438,12 @@ static grpc_endpoint_write_status grpc_tcp_flush(grpc_tcp *tcp) {
msg.msg_controllen = 0;
msg.msg_flags = 0;
GRPC_TIMER_MARK(SENDMSG_BEGIN, 0);
do {
/* TODO(klempner): Cork if this is a partial write */
sent_length = sendmsg(tcp->fd, &msg, 0);
} while (sent_length < 0 && errno == EINTR);
GRPC_TIMER_MARK(SENDMSG_END, 0);
if (sent_length < 0) {
if (errno == EAGAIN) {
@ -472,6 +479,7 @@ static void grpc_tcp_handle_write(void *arg /* grpc_tcp */, int success) {
return;
}
GRPC_TIMER_MARK(CB_WRITE_BEGIN, 0);
write_status = grpc_tcp_flush(tcp);
if (write_status == GRPC_ENDPOINT_WRITE_PENDING) {
grpc_fd_notify_on_write(tcp->em_fd, &tcp->write_closure);
@ -487,6 +495,7 @@ static void grpc_tcp_handle_write(void *arg /* grpc_tcp */, int success) {
cb(tcp->write_user_data, cb_status);
grpc_tcp_unref(tcp);
}
GRPC_TIMER_MARK(CB_WRITE_END, 0);
}
static grpc_endpoint_write_status grpc_tcp_write(grpc_endpoint *ep,
@ -509,6 +518,7 @@ static grpc_endpoint_write_status grpc_tcp_write(grpc_endpoint *ep,
}
}
GRPC_TIMER_MARK(WRITE_BEGIN, 0);
GPR_ASSERT(tcp->write_cb == NULL);
slice_state_init(&tcp->write_state, slices, nslices, nslices);
@ -522,6 +532,7 @@ static grpc_endpoint_write_status grpc_tcp_write(grpc_endpoint *ep,
grpc_fd_notify_on_write(tcp->em_fd, &tcp->write_closure);
}
GRPC_TIMER_MARK(WRITE_END, 0);
return status;
}

@ -174,7 +174,6 @@ void grpc_tcp_server_destroy(
while (s->active_ports) {
gpr_cv_wait(&s->cv, &s->mu, gpr_inf_future);
}
gpr_mu_unlock(&s->mu);
/* delete ALL the things */
if (s->nports) {
@ -185,7 +184,9 @@ void grpc_tcp_server_destroy(
}
grpc_fd_orphan(sp->emfd, destroyed_port, s);
}
gpr_mu_unlock(&s->mu);
} else {
gpr_mu_unlock(&s->mu);
finish_shutdown(s);
}
}

@ -191,13 +191,13 @@ static void start_accept(server_port *port) {
goto failure;
}
/* TODO(jtattermusch): probably a race here, we regularly get use-after-free on server shutdown */
GPR_ASSERT(port->socket != 0xfeeefeee);
success = port->AcceptEx(port->socket->socket, sock, port->addresses, 0,
addrlen, addrlen, &bytes_received,
&port->socket->read_info.overlapped);
if (success) {
gpr_log(GPR_DEBUG, "accepted immediately - but we still go to sleep");
} else {
if (!success) {
int error = WSAGetLastError();
if (error != ERROR_IO_PENDING) {
message = "AcceptEx failed: %s";
@ -234,11 +234,9 @@ static void on_accept(void *arg, int success) {
gpr_free(utf8_message);
closesocket(sock);
} else {
gpr_log(GPR_DEBUG, "on_accept: accepted connection");
ep = grpc_tcp_create(grpc_winsocket_create(sock));
}
} else {
gpr_log(GPR_DEBUG, "on_accept: shutting down");
closesocket(sock);
gpr_mu_lock(&sp->server->mu);
if (0 == --sp->server->active_ports) {
@ -248,7 +246,9 @@ static void on_accept(void *arg, int success) {
}
if (ep) sp->server->cb(sp->server->cb_arg, ep);
start_accept(sp);
if (success) {
start_accept(sp);
}
}
static int add_socket_to_server(grpc_tcp_server *s, SOCKET sock,

@ -93,14 +93,11 @@ typedef struct grpc_tcp {
} grpc_tcp;
static void tcp_ref(grpc_tcp *tcp) {
gpr_log(GPR_DEBUG, "tcp_ref");
gpr_ref(&tcp->refcount);
}
static void tcp_unref(grpc_tcp *tcp) {
gpr_log(GPR_DEBUG, "tcp_unref");
if (gpr_unref(&tcp->refcount)) {
gpr_log(GPR_DEBUG, "tcp_unref: destroying");
gpr_slice_buffer_destroy(&tcp->write_slices);
grpc_winsocket_orphan(tcp->socket);
gpr_free(tcp);
@ -126,24 +123,20 @@ static void on_read(void *tcpp, int success) {
return;
}
gpr_log(GPR_DEBUG, "on_read");
tcp->outstanding_read = 0;
if (socket->read_info.wsa_error != 0) {
char *utf8_message = gpr_format_message(info->wsa_error);
__debugbreak();
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) {
sub = gpr_slice_sub(tcp->read_slice, 0, info->bytes_transfered);
gpr_log(GPR_DEBUG, "on_read: calling callback");
status = GRPC_ENDPOINT_CB_OK;
slice = &sub;
nslices = 1;
} else {
gpr_log(GPR_DEBUG, "on_read: closed socket");
gpr_slice_unref(tcp->read_slice);
status = GRPC_ENDPOINT_CB_EOF;
}
@ -174,27 +167,22 @@ static void win_notify_on_read(grpc_endpoint *ep,
buffer.len = GPR_SLICE_LENGTH(tcp->read_slice);
buffer.buf = (char *)GPR_SLICE_START_PTR(tcp->read_slice);
gpr_log(GPR_DEBUG, "win_notify_on_read: calling WSARecv without overlap");
status = WSARecv(tcp->socket->socket, &buffer, 1, &bytes_read, &flags,
NULL, NULL);
info->wsa_error = status == 0 ? 0 : WSAGetLastError();
if (info->wsa_error != WSAEWOULDBLOCK) {
gpr_log(GPR_DEBUG, "got response immediately, calling on_read");
info->bytes_transfered = bytes_read;
/* This might heavily recurse. */
on_read(tcp, 1);
return;
}
gpr_log(GPR_DEBUG, "got WSAEWOULDBLOCK - calling WSARecv with overlap");
memset(&tcp->socket->read_info.overlapped, 0, sizeof(OVERLAPPED));
status = WSARecv(tcp->socket->socket, &buffer, 1, &bytes_read, &flags,
&info->overlapped, NULL);
if (status == 0) {
gpr_log(GPR_DEBUG, "got response immediately, but we're going to sleep");
grpc_socket_notify_on_read(tcp->socket, on_read, tcp);
return;
}
@ -213,7 +201,6 @@ static void win_notify_on_read(grpc_endpoint *ep,
return;
}
gpr_log(GPR_DEBUG, "waiting on the IO completion port now");
grpc_socket_notify_on_read(tcp->socket, on_read, tcp);
}
@ -227,8 +214,6 @@ static void on_write(void *tcpp, int success) {
GPR_ASSERT(tcp->outstanding_write);
gpr_log(GPR_DEBUG, "on_write");
if (!success) {
tcp_unref(tcp);
cb(opaque, GRPC_ENDPOINT_CB_SHUTDOWN);
@ -265,13 +250,9 @@ static grpc_endpoint_write_status win_write(grpc_endpoint *ep,
WSABUF *allocated = NULL;
WSABUF *buffers = local_buffers;
GPR_ASSERT(nslices != 0);
GPR_ASSERT(GPR_SLICE_LENGTH(slices[0]) != 0);
GPR_ASSERT(!tcp->outstanding_write);
tcp_ref(tcp);
gpr_log(GPR_DEBUG, "win_write");
tcp->outstanding_write = 1;
tcp->write_cb = cb;
tcp->write_user_data = arg;
@ -287,14 +268,12 @@ static grpc_endpoint_write_status win_write(grpc_endpoint *ep,
buffers[i].buf = (char *)GPR_SLICE_START_PTR(tcp->write_slices.slices[i]);
}
gpr_log(GPR_DEBUG, "win_write: calling WSASend without overlap");
status = WSASend(socket->socket, buffers, tcp->write_slices.count,
&bytes_sent, 0, NULL, NULL);
info->wsa_error = status == 0 ? 0 : WSAGetLastError();
if (info->wsa_error != WSAEWOULDBLOCK) {
grpc_endpoint_write_status ret = GRPC_ENDPOINT_WRITE_ERROR;
gpr_log(GPR_DEBUG, "got response immediately, cleaning up and leaving");
if (status == 0) {
ret = GRPC_ENDPOINT_WRITE_DONE;
GPR_ASSERT(bytes_sent == tcp->write_slices.length);
@ -310,8 +289,6 @@ static grpc_endpoint_write_status win_write(grpc_endpoint *ep,
return ret;
}
gpr_log(GPR_DEBUG, "got WSAEWOULDBLOCK - calling WSASend with overlap");
memset(&socket->write_info, 0, sizeof(OVERLAPPED));
status = WSASend(socket->socket, buffers, tcp->write_slices.count,
&bytes_sent, 0, &socket->write_info.overlapped, NULL);
@ -329,9 +306,6 @@ static grpc_endpoint_write_status win_write(grpc_endpoint *ep,
tcp_unref(tcp);
return GRPC_ENDPOINT_WRITE_ERROR;
}
gpr_log(GPR_DEBUG, "win_write: got pending op");
} else {
gpr_log(GPR_DEBUG, "wrote data immediately - but we're going to sleep");
}
grpc_socket_notify_on_write(socket, on_write, tcp);
@ -340,19 +314,16 @@ static grpc_endpoint_write_status win_write(grpc_endpoint *ep,
static void win_add_to_pollset(grpc_endpoint *ep, grpc_pollset *pollset) {
grpc_tcp *tcp = (grpc_tcp *) ep;
gpr_log(GPR_DEBUG, "win_add_to_pollset");
grpc_iocp_add_socket(tcp->socket);
}
static void win_shutdown(grpc_endpoint *ep) {
grpc_tcp *tcp = (grpc_tcp *) ep;
gpr_log(GPR_DEBUG, "win_shutdown");
grpc_winsocket_shutdown(tcp->socket);
}
static void win_destroy(grpc_endpoint *ep) {
grpc_tcp *tcp = (grpc_tcp *) ep;
gpr_log(GPR_DEBUG, "win_destroy");
tcp_unref(tcp);
}

@ -0,0 +1,138 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifdef GRPC_LATENCY_PROFILER
#include "src/core/profiling/timers.h"
#include "src/core/profiling/timers_preciseclock.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/time.h>
#include <grpc/support/sync.h>
#include <stdio.h>
typedef struct grpc_timer_entry {
grpc_precise_clock tm;
const char* tag;
void* id;
const char* file;
int line;
} grpc_timer_entry;
struct grpc_timers_log {
gpr_mu mu;
grpc_timer_entry* log;
int num_entries;
int capacity;
int capacity_limit;
FILE* fp;
};
grpc_timers_log* grpc_timers_log_global = NULL;
grpc_timers_log* grpc_timers_log_create(int capacity_limit, FILE* dump) {
grpc_timers_log* log = gpr_malloc(sizeof(*log));
/* TODO (vpai): Allow allocation below limit */
log->log = gpr_malloc(capacity_limit * sizeof(*log->log));
/* TODO (vpai): Improve concurrency, do per-thread logging? */
gpr_mu_init(&log->mu);
log->num_entries = 0;
log->capacity = log->capacity_limit = capacity_limit;
log->fp = dump;
return log;
}
static void log_report_locked(grpc_timers_log* log) {
FILE* fp = log->fp;
int i;
for (i = 0; i < log->num_entries; i++) {
grpc_timer_entry* entry = &(log->log[i]);
fprintf(fp, "GRPC_LAT_PROF ");
grpc_precise_clock_print(&entry->tm, fp);
fprintf(fp, " %s %p %s %d\n", entry->tag, entry->id, entry->file,
entry->line);
}
/* Now clear out the log */
log->num_entries = 0;
}
void grpc_timers_log_destroy(grpc_timers_log* log) {
gpr_mu_lock(&log->mu);
log_report_locked(log);
gpr_mu_unlock(&log->mu);
gpr_free(log->log);
gpr_mu_destroy(&log->mu);
gpr_free(log);
}
void grpc_timers_log_add(grpc_timers_log* log, const char* tag, void* id,
const char* file, int line) {
grpc_timer_entry* entry;
/* TODO (vpai) : Improve concurrency */
gpr_mu_lock(&log->mu);
if (log->num_entries == log->capacity_limit) {
log_report_locked(log);
}
entry = &log->log[log->num_entries++];
grpc_precise_clock_now(&entry->tm);
entry->tag = tag;
entry->id = id;
entry->file = file;
entry->line = line;
gpr_mu_unlock(&log->mu);
}
void grpc_timers_log_global_init(void) {
grpc_timers_log_global = grpc_timers_log_create(100000, stdout);
}
void grpc_timers_log_global_destroy(void) {
grpc_timers_log_destroy(grpc_timers_log_global);
}
#else /* !GRPC_LATENCY_PROFILER */
void grpc_timers_log_global_init(void) {}
void grpc_timers_log_global_destroy(void) {}
#endif /* GRPC_LATENCY_PROFILER */

@ -0,0 +1,71 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef GRPC_CORE_PROFILING_TIMERS_H
#define GRPC_CORE_PROFILING_TIMERS_H
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifdef GRPC_LATENCY_PROFILER
typedef struct grpc_timers_log grpc_timers_log;
grpc_timers_log* grpc_timers_log_create(int capacity_limit, FILE* dump);
void grpc_timers_log_add(grpc_timers_log*, const char* tag, void* id,
const char* file, int line);
void grpc_timers_log_destroy(grpc_timers_log *);
extern grpc_timers_log *grpc_timers_log_global;
#define GRPC_TIMER_MARK(x, s) \
grpc_timers_log_add(grpc_timers_log_global, #x, ((void *)(gpr_intptr)(s)), \
__FILE__, __LINE__)
#else /* !GRPC_LATENCY_PROFILER */
#define GRPC_TIMER_MARK(x, s) \
do { \
} while (0)
#endif /* GRPC_LATENCY_PROFILER */
void grpc_timers_log_global_init(void);
void grpc_timers_log_global_destroy(void);
#ifdef __cplusplus
}
#endif
#endif /* GRPC_CORE_PROFILING_TIMERS_H */

@ -31,22 +31,26 @@
*
*/
#include <string.h>
#ifndef GRPC_CORE_PROFILING_TIMERS_PRECISECLOCK_H
#define GRPC_CORE_PROFILING_TIMERS_PRECISECLOCK_H
#include <grpc/grpc.h>
#include "src/core/security/credentials.h"
#include "src/core/security/security_context.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/useful.h>
#include <grpc/support/time.h>
#include <stdio.h>
grpc_channel *grpc_secure_channel_create(grpc_credentials *creds,
const char *target,
const grpc_channel_args *args) {
grpc_secure_channel_factory factories[] = {
{GRPC_CREDENTIALS_TYPE_SSL, grpc_ssl_channel_create},
{GRPC_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY,
grpc_fake_transport_security_channel_create}};
return grpc_secure_channel_create_with_factories(
factories, GPR_ARRAY_SIZE(factories), creds, target, args);
typedef struct grpc_precise_clock grpc_precise_clock;
#ifdef GRPC_TIMERS_RDTSC
#error RDTSC timers not currently supported
#else
struct grpc_precise_clock {
gpr_timespec clock;
};
static void grpc_precise_clock_now(grpc_precise_clock* clk) {
clk->clock = gpr_now();
}
static void grpc_precise_clock_print(const grpc_precise_clock* clk, FILE* fp) {
fprintf(fp, "%ld.%09d", clk->clock.tv_sec, clk->clock.tv_nsec);
}
#endif /* GRPC_TIMERS_RDTSC */
#endif /* GRPC_CORE_PROFILING_TIMERS_PRECISECLOCK_H */

@ -40,21 +40,24 @@
#include "src/core/support/string.h"
#include "src/core/channel/channel_stack.h"
#include "src/core/security/security_context.h"
#include "src/core/security/security_connector.h"
#include "src/core/security/credentials.h"
#include "src/core/surface/call.h"
#define MAX_CREDENTIALS_METADATA_COUNT 4
/* We can have a per-call credentials. */
typedef struct {
grpc_credentials *creds;
grpc_mdstr *host;
grpc_mdstr *method;
grpc_call_op op;
grpc_linked_mdelem md_links[MAX_CREDENTIALS_METADATA_COUNT];
} call_data;
/* We can have a per-channel credentials. */
typedef struct {
grpc_channel_security_context *security_context;
grpc_channel_security_connector *security_connector;
grpc_mdctx *md_ctx;
grpc_mdstr *authority_string;
grpc_mdstr *path_string;
@ -62,30 +65,8 @@ typedef struct {
grpc_mdstr *status_key;
} channel_data;
static void do_nothing(void *ignored, grpc_op_error error) {}
static void bubbleup_error(grpc_call_element *elem, const char *error_msg) {
grpc_call_op finish_op;
channel_data *channeld = elem->channel_data;
char status[GPR_LTOA_MIN_BUFSIZE];
gpr_log(GPR_ERROR, "%s", error_msg);
finish_op.type = GRPC_RECV_METADATA;
finish_op.dir = GRPC_CALL_UP;
finish_op.flags = 0;
finish_op.data.metadata = grpc_mdelem_from_metadata_strings(
channeld->md_ctx, grpc_mdstr_ref(channeld->error_msg_key),
grpc_mdstr_from_string(channeld->md_ctx, error_msg));
finish_op.done_cb = do_nothing;
finish_op.user_data = NULL;
grpc_call_next_op(elem, &finish_op);
gpr_ltoa(GRPC_STATUS_UNAUTHENTICATED, status);
finish_op.data.metadata = grpc_mdelem_from_metadata_strings(
channeld->md_ctx, grpc_mdstr_ref(channeld->status_key),
grpc_mdstr_from_string(channeld->md_ctx, status));
grpc_call_next_op(elem, &finish_op);
grpc_call_element_recv_status(elem, GRPC_STATUS_UNAUTHENTICATED, error_msg);
grpc_call_element_send_cancel(elem);
}
@ -93,11 +74,15 @@ static void on_credentials_metadata(void *user_data, grpc_mdelem **md_elems,
size_t num_md,
grpc_credentials_status status) {
grpc_call_element *elem = (grpc_call_element *)user_data;
call_data *calld = elem->call_data;
grpc_call_op op = calld->op;
size_t i;
GPR_ASSERT(num_md <= MAX_CREDENTIALS_METADATA_COUNT);
for (i = 0; i < num_md; i++) {
grpc_call_element_send_metadata(elem, grpc_mdelem_ref(md_elems[i]));
grpc_metadata_batch_add_tail(&op.data.metadata, &calld->md_links[i],
grpc_mdelem_ref(md_elems[i]));
}
grpc_call_next_op(elem, &((call_data *)elem->call_data)->op);
grpc_call_next_op(elem, &op);
}
static char *build_service_url(const char *url_scheme, call_data *calld) {
@ -126,7 +111,7 @@ static void send_security_metadata(grpc_call_element *elem, grpc_call_op *op) {
channel_data *channeld = elem->channel_data;
grpc_credentials *channel_creds =
channeld->security_context->request_metadata_creds;
channeld->security_connector->request_metadata_creds;
/* TODO(jboeuf):
Decide on the policy in this case:
- populate both channel and call?
@ -138,7 +123,7 @@ static void send_security_metadata(grpc_call_element *elem, grpc_call_op *op) {
if (channel_creds != NULL &&
grpc_credentials_has_request_metadata(channel_creds)) {
char *service_url =
build_service_url(channeld->security_context->base.url_scheme, calld);
build_service_url(channeld->security_connector->base.url_scheme, calld);
calld->op = *op; /* Copy op (originates from the caller's stack). */
grpc_credentials_get_request_metadata(channel_creds, service_url,
on_credentials_metadata, elem);
@ -159,6 +144,7 @@ static void on_host_checked(void *user_data, grpc_security_status status) {
gpr_asprintf(&error_msg, "Invalid host %s set in :authority metadata.",
grpc_mdstr_as_c_string(calld->host));
bubbleup_error(elem, error_msg);
grpc_metadata_batch_destroy(&calld->op.data.metadata);
gpr_free(error_msg);
calld->op.done_cb(calld->op.user_data, GRPC_OP_ERROR);
}
@ -174,27 +160,28 @@ static void call_op(grpc_call_element *elem, grpc_call_element *from_elem,
/* grab pointers to our data from the call element */
call_data *calld = elem->call_data;
channel_data *channeld = elem->channel_data;
grpc_linked_mdelem *l;
switch (op->type) {
case GRPC_SEND_METADATA:
/* Pointer comparison is OK for md_elems created from the same context. */
if (op->data.metadata->key == channeld->authority_string) {
if (calld->host != NULL) grpc_mdstr_unref(calld->host);
calld->host = grpc_mdstr_ref(op->data.metadata->value);
} else if (op->data.metadata->key == channeld->path_string) {
if (calld->method != NULL) grpc_mdstr_unref(calld->method);
calld->method = grpc_mdstr_ref(op->data.metadata->value);
for (l = op->data.metadata.list.head; l != NULL; l = l->next) {
grpc_mdelem *md = l->md;
/* Pointer comparison is OK for md_elems created from the same context.
*/
if (md->key == channeld->authority_string) {
if (calld->host != NULL) grpc_mdstr_unref(calld->host);
calld->host = grpc_mdstr_ref(md->value);
} else if (md->key == channeld->path_string) {
if (calld->method != NULL) grpc_mdstr_unref(calld->method);
calld->method = grpc_mdstr_ref(md->value);
}
}
grpc_call_next_op(elem, op);
break;
case GRPC_SEND_START:
if (calld->host != NULL) {
grpc_security_status status;
const char *call_host = grpc_mdstr_as_c_string(calld->host);
calld->op = *op; /* Copy op (originates from the caller's stack). */
status = grpc_channel_security_context_check_call_host(
channeld->security_context, call_host, on_host_checked, elem);
status = grpc_channel_security_connector_check_call_host(
channeld->security_connector, call_host, on_host_checked, elem);
if (status != GRPC_SECURITY_OK) {
if (status == GRPC_SECURITY_ERROR) {
char *error_msg;
@ -202,6 +189,7 @@ static void call_op(grpc_call_element *elem, grpc_call_element *from_elem,
"Invalid host %s set in :authority metadata.",
call_host);
bubbleup_error(elem, error_msg);
grpc_metadata_batch_destroy(&calld->op.data.metadata);
gpr_free(error_msg);
op->done_cb(op->user_data, GRPC_OP_ERROR);
}
@ -210,7 +198,6 @@ static void call_op(grpc_call_element *elem, grpc_call_element *from_elem,
}
send_security_metadata(elem, op);
break;
default:
/* pass control up or down the stack depending on op->dir */
grpc_call_next_op(elem, op);
@ -255,7 +242,7 @@ static void init_channel_elem(grpc_channel_element *elem,
const grpc_channel_args *args,
grpc_mdctx *metadata_context, int is_first,
int is_last) {
grpc_security_context *ctx = grpc_find_security_context_in_args(args);
grpc_security_connector *ctx = grpc_find_security_connector_in_args(args);
/* grab pointers to our data from the channel element */
channel_data *channeld = elem->channel_data;
@ -268,23 +255,24 @@ static void init_channel_elem(grpc_channel_element *elem,
/* initialize members */
GPR_ASSERT(ctx->is_client_side);
channeld->security_context =
(grpc_channel_security_context *)grpc_security_context_ref(ctx);
channeld->security_connector =
(grpc_channel_security_connector *)grpc_security_connector_ref(ctx);
channeld->md_ctx = metadata_context;
channeld->authority_string =
grpc_mdstr_from_string(channeld->md_ctx, ":authority");
channeld->path_string = grpc_mdstr_from_string(channeld->md_ctx, ":path");
channeld->error_msg_key =
grpc_mdstr_from_string(channeld->md_ctx, "grpc-message");
channeld->status_key = grpc_mdstr_from_string(channeld->md_ctx, "grpc-status");
channeld->status_key =
grpc_mdstr_from_string(channeld->md_ctx, "grpc-status");
}
/* Destructor for channel data */
static void destroy_channel_elem(grpc_channel_element *elem) {
/* grab pointers to our data from the channel element */
channel_data *channeld = elem->channel_data;
grpc_channel_security_context *ctx = channeld->security_context;
if (ctx != NULL) grpc_security_context_unref(&ctx->base);
grpc_channel_security_connector *ctx = channeld->security_connector;
if (ctx != NULL) grpc_security_connector_unref(&ctx->base);
if (channeld->authority_string != NULL) {
grpc_mdstr_unref(channeld->authority_string);
}
@ -300,6 +288,5 @@ static void destroy_channel_elem(grpc_channel_element *elem) {
}
const grpc_channel_filter grpc_client_auth_filter = {
call_op, channel_op, sizeof(call_data),
init_call_elem, destroy_call_elem, sizeof(channel_data),
init_channel_elem, destroy_channel_elem, "auth"};
call_op, channel_op, sizeof(call_data), init_call_elem, destroy_call_elem,
sizeof(channel_data), init_channel_elem, destroy_channel_elem, "auth"};

@ -36,11 +36,14 @@
#include <string.h>
#include <stdio.h>
#include "src/core/channel/channel_args.h"
#include "src/core/channel/http_client_filter.h"
#include "src/core/json/json.h"
#include "src/core/httpcli/httpcli.h"
#include "src/core/iomgr/iomgr.h"
#include "src/core/security/json_token.h"
#include "src/core/support/string.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/sync.h>
@ -111,11 +114,49 @@ void grpc_credentials_get_request_metadata(grpc_credentials *creds,
creds->vtable->get_request_metadata(creds, service_url, cb, user_data);
}
grpc_mdctx *grpc_credentials_get_or_create_metadata_context(
grpc_credentials *creds) {
grpc_mdctx *mdctx = NULL;
if (creds != NULL && creds->vtable->get_metadata_context != NULL) {
mdctx = creds->vtable->get_metadata_context(creds);
}
if (mdctx == NULL) {
return grpc_mdctx_create();
} else {
grpc_mdctx_ref(mdctx);
return mdctx;
}
}
grpc_security_status grpc_credentials_create_security_connector(
grpc_credentials *creds, const char *target, const grpc_channel_args *args,
grpc_credentials *request_metadata_creds,
grpc_channel_security_connector **sc, grpc_channel_args **new_args) {
*new_args = NULL;
if (creds == NULL || creds->vtable->create_security_connector == NULL ||
grpc_credentials_has_request_metadata_only(creds)) {
gpr_log(GPR_ERROR,
"Invalid credentials for creating a security connector.");
return GRPC_SECURITY_ERROR;
}
return creds->vtable->create_security_connector(
creds, target, args, request_metadata_creds, sc, new_args);
}
void grpc_server_credentials_release(grpc_server_credentials *creds) {
if (creds == NULL) return;
creds->vtable->destroy(creds);
}
grpc_security_status grpc_server_credentials_create_security_connector(
grpc_server_credentials *creds, grpc_security_connector **sc) {
if (creds == NULL || creds->vtable->create_security_connector == NULL) {
gpr_log(GPR_ERROR, "Server credentials cannot create security context.");
return GRPC_SECURITY_ERROR;
}
return creds->vtable->create_security_connector(creds, sc);
}
/* -- Ssl credentials. -- */
typedef struct {
@ -167,31 +208,53 @@ static int ssl_has_request_metadata_only(const grpc_credentials *creds) {
return 0;
}
static grpc_credentials_vtable ssl_vtable = {
ssl_destroy, ssl_has_request_metadata, ssl_has_request_metadata_only, NULL};
static grpc_server_credentials_vtable ssl_server_vtable = {ssl_server_destroy};
static grpc_mdctx *ssl_get_metadata_context(grpc_credentials *creds) {
return NULL;
}
const grpc_ssl_config *grpc_ssl_credentials_get_config(
const grpc_credentials *creds) {
if (creds == NULL || strcmp(creds->type, GRPC_CREDENTIALS_TYPE_SSL)) {
return NULL;
} else {
grpc_ssl_credentials *c = (grpc_ssl_credentials *)creds;
return &c->config;
static grpc_security_status ssl_create_security_connector(
grpc_credentials *creds, const char *target, const grpc_channel_args *args,
grpc_credentials *request_metadata_creds,
grpc_channel_security_connector **sc, grpc_channel_args **new_args) {
grpc_ssl_credentials *c = (grpc_ssl_credentials *)creds;
grpc_security_status status = GRPC_SECURITY_OK;
size_t i = 0;
const char *overridden_target_name = NULL;
grpc_arg arg;
for (i = 0; args && i < args->num_args; i++) {
grpc_arg *arg = &args->args[i];
if (strcmp(arg->key, GRPC_SSL_TARGET_NAME_OVERRIDE_ARG) == 0 &&
arg->type == GRPC_ARG_STRING) {
overridden_target_name = arg->value.string;
break;
}
}
status = grpc_ssl_channel_security_connector_create(
request_metadata_creds, &c->config, target, overridden_target_name, sc);
if (status != GRPC_SECURITY_OK) {
return status;
}
arg.type = GRPC_ARG_STRING;
arg.key = GRPC_ARG_HTTP2_SCHEME;
arg.value.string = "https";
*new_args = grpc_channel_args_copy_and_add(args, &arg);
return status;
}
const grpc_ssl_server_config *grpc_ssl_server_credentials_get_config(
const grpc_server_credentials *creds) {
if (creds == NULL || strcmp(creds->type, GRPC_CREDENTIALS_TYPE_SSL)) {
return NULL;
} else {
grpc_ssl_server_credentials *c = (grpc_ssl_server_credentials *)creds;
return &c->config;
}
static grpc_security_status ssl_server_create_security_connector(
grpc_server_credentials *creds, grpc_security_connector **sc) {
grpc_ssl_server_credentials *c = (grpc_ssl_server_credentials *)creds;
return grpc_ssl_server_security_connector_create(&c->config, sc);
}
static grpc_credentials_vtable ssl_vtable = {
ssl_destroy, ssl_has_request_metadata, ssl_has_request_metadata_only,
ssl_get_metadata_context, NULL, ssl_create_security_connector};
static grpc_server_credentials_vtable ssl_server_vtable = {
ssl_server_destroy, ssl_server_create_security_connector};
static void ssl_copy_key_material(const char *input, unsigned char **output,
size_t *output_size) {
*output_size = strlen(input);
@ -371,9 +434,14 @@ static void jwt_get_request_metadata(grpc_credentials *creds,
}
}
static grpc_mdctx *jwt_get_metadata_context(grpc_credentials *creds) {
grpc_jwt_credentials *c = (grpc_jwt_credentials *)creds;
return c->md_ctx;
}
static grpc_credentials_vtable jwt_vtable = {
jwt_destroy, jwt_has_request_metadata, jwt_has_request_metadata_only,
jwt_get_request_metadata};
jwt_get_metadata_context, jwt_get_request_metadata, NULL};
grpc_credentials *grpc_jwt_credentials_create(const char *json_key,
gpr_timespec token_lifetime) {
@ -585,12 +653,20 @@ static void init_oauth2_token_fetcher(grpc_oauth2_token_fetcher_credentials *c,
c->fetch_func = fetch_func;
}
static grpc_mdctx *oauth2_token_fetcher_get_metadata_context(
grpc_credentials *creds) {
grpc_oauth2_token_fetcher_credentials *c =
(grpc_oauth2_token_fetcher_credentials *)creds;
return c->md_ctx;
}
/* -- ComputeEngine credentials. -- */
static grpc_credentials_vtable compute_engine_vtable = {
oauth2_token_fetcher_destroy, oauth2_token_fetcher_has_request_metadata,
oauth2_token_fetcher_has_request_metadata_only,
oauth2_token_fetcher_get_request_metadata};
oauth2_token_fetcher_get_metadata_context,
oauth2_token_fetcher_get_request_metadata, NULL};
static void compute_engine_fetch_oauth2(
grpc_credentials_metadata_request *metadata_req,
@ -633,7 +709,8 @@ static void service_account_destroy(grpc_credentials *creds) {
static grpc_credentials_vtable service_account_vtable = {
service_account_destroy, oauth2_token_fetcher_has_request_metadata,
oauth2_token_fetcher_has_request_metadata_only,
oauth2_token_fetcher_get_request_metadata};
oauth2_token_fetcher_get_metadata_context,
oauth2_token_fetcher_get_request_metadata, NULL};
static void service_account_fetch_oauth2(
grpc_credentials_metadata_request *metadata_req,
@ -706,7 +783,8 @@ static void refresh_token_destroy(grpc_credentials *creds) {
static grpc_credentials_vtable refresh_token_vtable = {
refresh_token_destroy, oauth2_token_fetcher_has_request_metadata,
oauth2_token_fetcher_has_request_metadata_only,
oauth2_token_fetcher_get_request_metadata};
oauth2_token_fetcher_get_metadata_context,
oauth2_token_fetcher_get_request_metadata, NULL};
static void refresh_token_fetch_oauth2(
grpc_credentials_metadata_request *metadata_req,
@ -801,9 +879,15 @@ static void fake_oauth2_get_request_metadata(grpc_credentials *creds,
}
}
static grpc_mdctx *fake_oauth2_get_metadata_context(grpc_credentials *creds) {
grpc_fake_oauth2_credentials *c = (grpc_fake_oauth2_credentials *)creds;
return c->md_ctx;
}
static grpc_credentials_vtable fake_oauth2_vtable = {
fake_oauth2_destroy, fake_oauth2_has_request_metadata,
fake_oauth2_has_request_metadata_only, fake_oauth2_get_request_metadata};
fake_oauth2_has_request_metadata_only, fake_oauth2_get_metadata_context,
fake_oauth2_get_request_metadata, NULL};
grpc_credentials *grpc_fake_oauth2_credentials_create(
const char *token_md_value, int is_async) {
@ -842,14 +926,38 @@ static int fake_transport_security_has_request_metadata_only(
return 0;
}
static grpc_mdctx *fake_transport_security_get_metadata_context(
grpc_credentials *c) {
return NULL;
}
static grpc_security_status
fake_transport_security_create_security_connector(
grpc_credentials *c, const char *target, const grpc_channel_args *args,
grpc_credentials *request_metadata_creds,
grpc_channel_security_connector **sc, grpc_channel_args **new_args) {
*sc = grpc_fake_channel_security_connector_create(request_metadata_creds, 1);
return GRPC_SECURITY_OK;
}
static grpc_security_status
fake_transport_security_server_create_security_connector(
grpc_server_credentials *c, grpc_security_connector **sc) {
*sc = grpc_fake_server_security_connector_create();
return GRPC_SECURITY_OK;
}
static grpc_credentials_vtable fake_transport_security_credentials_vtable = {
fake_transport_security_credentials_destroy,
fake_transport_security_has_request_metadata,
fake_transport_security_has_request_metadata_only, NULL};
fake_transport_security_has_request_metadata_only,
fake_transport_security_get_metadata_context, NULL,
fake_transport_security_create_security_connector};
static grpc_server_credentials_vtable
fake_transport_security_server_credentials_vtable = {
fake_transport_security_server_credentials_destroy};
fake_transport_security_server_credentials_destroy,
fake_transport_security_server_create_security_connector};
grpc_credentials *grpc_fake_transport_security_credentials_create(void) {
grpc_credentials *c = gpr_malloc(sizeof(grpc_credentials));
@ -874,6 +982,7 @@ grpc_server_credentials *grpc_fake_transport_security_server_credentials_create(
typedef struct {
grpc_credentials base;
grpc_credentials_array inner;
grpc_credentials *connector_creds;
} grpc_composite_credentials;
typedef struct {
@ -995,9 +1104,43 @@ static void composite_get_request_metadata(grpc_credentials *creds,
GPR_ASSERT(0); /* Should have exited before. */
}
static grpc_mdctx *composite_get_metadata_context(grpc_credentials *creds) {
grpc_composite_credentials *c = (grpc_composite_credentials *)creds;
grpc_mdctx *ctx = NULL;
size_t i;
for (i = 0; i < c->inner.num_creds; i++) {
grpc_credentials *inner_creds = c->inner.creds_array[i];
grpc_mdctx *inner_ctx = NULL;
if (inner_creds->vtable->get_metadata_context != NULL) {
inner_ctx = inner_creds->vtable->get_metadata_context(inner_creds);
}
if (inner_ctx) {
GPR_ASSERT(ctx == NULL &&
"can only have one metadata context per composite credential");
ctx = inner_ctx;
}
}
return ctx;
}
static grpc_security_status composite_create_security_connector(
grpc_credentials *creds, const char *target, const grpc_channel_args *args,
grpc_credentials *request_metadata_creds,
grpc_channel_security_connector **sc, grpc_channel_args **new_args) {
grpc_composite_credentials *c = (grpc_composite_credentials *)creds;
if (c->connector_creds == NULL) {
gpr_log(GPR_ERROR,
"Cannot create security connector, missing connector credentials.");
return GRPC_SECURITY_ERROR;
}
return grpc_credentials_create_security_connector(c->connector_creds, target,
args, creds, sc, new_args);
}
static grpc_credentials_vtable composite_credentials_vtable = {
composite_destroy, composite_has_request_metadata,
composite_has_request_metadata_only, composite_get_request_metadata};
composite_has_request_metadata_only, composite_get_metadata_context,
composite_get_request_metadata, composite_create_security_connector};
static grpc_credentials_array get_creds_array(grpc_credentials **creds_addr) {
grpc_credentials_array result;
@ -1013,6 +1156,7 @@ static grpc_credentials_array get_creds_array(grpc_credentials **creds_addr) {
grpc_credentials *grpc_composite_credentials_create(grpc_credentials *creds1,
grpc_credentials *creds2) {
size_t i;
size_t creds_array_byte_size;
grpc_credentials_array creds1_array;
grpc_credentials_array creds2_array;
grpc_composite_credentials *c;
@ -1026,16 +1170,39 @@ grpc_credentials *grpc_composite_credentials_create(grpc_credentials *creds1,
creds1_array = get_creds_array(&creds1);
creds2_array = get_creds_array(&creds2);
c->inner.num_creds = creds1_array.num_creds + creds2_array.num_creds;
c->inner.creds_array =
gpr_malloc(c->inner.num_creds * sizeof(grpc_credentials *));
creds_array_byte_size = c->inner.num_creds * sizeof(grpc_credentials *);
c->inner.creds_array = gpr_malloc(creds_array_byte_size);
memset(c->inner.creds_array, 0, creds_array_byte_size);
for (i = 0; i < creds1_array.num_creds; i++) {
c->inner.creds_array[i] = grpc_credentials_ref(creds1_array.creds_array[i]);
grpc_credentials *cur_creds = creds1_array.creds_array[i];
if (!grpc_credentials_has_request_metadata_only(cur_creds)) {
if (c->connector_creds == NULL) {
c->connector_creds = cur_creds;
} else {
gpr_log(GPR_ERROR, "Cannot compose multiple connector credentials.");
goto fail;
}
}
c->inner.creds_array[i] = grpc_credentials_ref(cur_creds);
}
for (i = 0; i < creds2_array.num_creds; i++) {
grpc_credentials *cur_creds = creds2_array.creds_array[i];
if (!grpc_credentials_has_request_metadata_only(cur_creds)) {
if (c->connector_creds == NULL) {
c->connector_creds = cur_creds;
} else {
gpr_log(GPR_ERROR, "Cannot compose multiple connector credentials.");
goto fail;
}
}
c->inner.creds_array[i + creds1_array.num_creds] =
grpc_credentials_ref(creds2_array.creds_array[i]);
grpc_credentials_ref(cur_creds);
}
return &c->base;
fail:
grpc_credentials_unref(&c->base);
return NULL;
}
const grpc_credentials_array *grpc_composite_credentials_get_credentials(
@ -1102,9 +1269,14 @@ static void iam_get_request_metadata(grpc_credentials *creds,
cb(user_data, md_array, 2, GRPC_CREDENTIALS_OK);
}
static grpc_mdctx *iam_get_metadata_context(grpc_credentials *creds) {
grpc_iam_credentials *c = (grpc_iam_credentials *)creds;
return c->md_ctx;
}
static grpc_credentials_vtable iam_vtable = {
iam_destroy, iam_has_request_metadata, iam_has_request_metadata_only,
iam_get_request_metadata};
iam_get_metadata_context, iam_get_request_metadata, NULL};
grpc_credentials *grpc_iam_credentials_create(const char *token,
const char *authority_selector) {

@ -39,6 +39,8 @@
#include <grpc/grpc_security.h>
#include <grpc/support/sync.h>
#include "src/core/security/security_connector.h"
struct grpc_httpcli_response;
/* --- Constants. --- */
@ -94,10 +96,16 @@ typedef struct {
void (*destroy)(grpc_credentials *c);
int (*has_request_metadata)(const grpc_credentials *c);
int (*has_request_metadata_only)(const grpc_credentials *c);
grpc_mdctx *(*get_metadata_context)(grpc_credentials *c);
void (*get_request_metadata)(grpc_credentials *c,
const char *service_url,
grpc_credentials_metadata_cb cb,
void *user_data);
grpc_security_status (*create_security_connector)(
grpc_credentials *c, const char *target, const grpc_channel_args *args,
grpc_credentials *request_metadata_creds,
grpc_channel_security_connector **sc, grpc_channel_args **new_args);
} grpc_credentials_vtable;
struct grpc_credentials {
@ -114,17 +122,20 @@ void grpc_credentials_get_request_metadata(grpc_credentials *creds,
const char *service_url,
grpc_credentials_metadata_cb cb,
void *user_data);
typedef struct {
unsigned char *pem_private_key;
size_t pem_private_key_size;
unsigned char *pem_cert_chain;
size_t pem_cert_chain_size;
unsigned char *pem_root_certs;
size_t pem_root_certs_size;
} grpc_ssl_config;
const grpc_ssl_config *grpc_ssl_credentials_get_config(
const grpc_credentials *ssl_creds);
/* Gets the mdctx from the credentials and increase the refcount if it exists,
otherwise, create a new one. */
grpc_mdctx *grpc_credentials_get_or_create_metadata_context(
grpc_credentials *creds);
/* Creates a security connector for the channel. May also create new channel
args for the channel to be used in place of the passed in const args if
returned non NULL. In that case the caller is responsible for destroying
new_args after channel creation. */
grpc_security_status grpc_credentials_create_security_connector(
grpc_credentials *creds, const char *target, const grpc_channel_args *args,
grpc_credentials *request_metadata_creds,
grpc_channel_security_connector **sc, grpc_channel_args **new_args);
typedef struct {
grpc_credentials **creds_array;
@ -156,6 +167,8 @@ grpc_credentials *grpc_fake_oauth2_credentials_create(
typedef struct {
void (*destroy)(grpc_server_credentials *c);
grpc_security_status (*create_security_connector)(
grpc_server_credentials *c, grpc_security_connector **sc);
} grpc_server_credentials_vtable;
struct grpc_server_credentials {
@ -163,17 +176,7 @@ struct grpc_server_credentials {
const char *type;
};
typedef struct {
unsigned char **pem_private_keys;
size_t *pem_private_keys_sizes;
unsigned char **pem_cert_chains;
size_t *pem_cert_chains_sizes;
size_t num_key_cert_pairs;
unsigned char *pem_root_certs;
size_t pem_root_certs_size;
} grpc_ssl_server_config;
const grpc_ssl_server_config *grpc_ssl_server_credentials_get_config(
const grpc_server_credentials *ssl_creds);
grpc_security_status grpc_server_credentials_create_security_connector(
grpc_server_credentials *creds, grpc_security_connector **sc);
#endif /* GRPC_INTERNAL_CORE_SECURITY_CREDENTIALS_H */

@ -127,7 +127,7 @@ static grpc_credentials *create_jwt_creds_from_path(char *creds_path) {
gpr_slice creds_data;
int file_ok = 0;
if (creds_path == NULL) return NULL;
creds_data = gpr_load_file(creds_path, &file_ok);
creds_data = gpr_load_file(creds_path, 1, &file_ok);
gpr_free(creds_path);
if (file_ok) {
result = grpc_jwt_credentials_create(
@ -145,7 +145,7 @@ static grpc_credentials *create_refresh_token_creds_from_path(
gpr_slice creds_data;
int file_ok = 0;
if (creds_path == NULL) return NULL;
creds_data = gpr_load_file(creds_path, &file_ok);
creds_data = gpr_load_file(creds_path, 1, &file_ok);
gpr_free(creds_path);
if (file_ok) {
result = grpc_refresh_token_credentials_create(

@ -43,7 +43,7 @@
#define GRPC_INITIAL_HANDSHAKE_BUFFER_SIZE 256
typedef struct {
grpc_security_context *ctx;
grpc_security_connector *connector;
tsi_handshaker *handshaker;
unsigned char *handshake_buffer;
size_t handshake_buffer_size;
@ -74,7 +74,7 @@ static void secure_transport_setup_done(grpc_secure_transport_setup *s,
if (s->handshaker != NULL) tsi_handshaker_destroy(s->handshaker);
if (s->handshake_buffer != NULL) gpr_free(s->handshake_buffer);
gpr_slice_buffer_destroy(&s->left_overs);
grpc_security_context_unref(s->ctx);
grpc_security_connector_unref(s->connector);
gpr_free(s);
}
@ -112,8 +112,8 @@ static void check_peer(grpc_secure_transport_setup *s) {
secure_transport_setup_done(s, 0);
return;
}
peer_status =
grpc_security_context_check_peer(s->ctx, peer, on_peer_checked, s);
peer_status = grpc_security_connector_check_peer(s->connector, peer,
on_peer_checked, s);
if (peer_status == GRPC_SECURITY_ERROR) {
gpr_log(GPR_ERROR, "Peer check failed.");
secure_transport_setup_done(s, 0);
@ -262,7 +262,7 @@ static void on_handshake_data_sent_to_peer(void *setup,
}
}
void grpc_setup_secure_transport(grpc_security_context *ctx,
void grpc_setup_secure_transport(grpc_security_connector *connector,
grpc_endpoint *nonsecure_endpoint,
grpc_secure_transport_setup_done_cb cb,
void *user_data) {
@ -270,12 +270,12 @@ void grpc_setup_secure_transport(grpc_security_context *ctx,
grpc_secure_transport_setup *s =
gpr_malloc(sizeof(grpc_secure_transport_setup));
memset(s, 0, sizeof(grpc_secure_transport_setup));
result = grpc_security_context_create_handshaker(ctx, &s->handshaker);
result = grpc_security_connector_create_handshaker(connector, &s->handshaker);
if (result != GRPC_SECURITY_OK) {
secure_transport_setup_done(s, 0);
return;
}
s->ctx = grpc_security_context_ref(ctx);
s->connector = grpc_security_connector_ref(connector);
s->handshake_buffer_size = GRPC_INITIAL_HANDSHAKE_BUFFER_SIZE;
s->handshake_buffer = gpr_malloc(s->handshake_buffer_size);
s->endpoint = nonsecure_endpoint;

@ -35,7 +35,7 @@
#define GRPC_INTERNAL_CORE_SECURITY_SECURE_TRANSPORT_SETUP_H
#include "src/core/iomgr/endpoint.h"
#include "src/core/security/security_context.h"
#include "src/core/security/security_connector.h"
/* --- Secure transport setup --- */
@ -45,7 +45,7 @@ typedef void (*grpc_secure_transport_setup_done_cb)(
grpc_endpoint *secure_endpoint);
/* Calls the callback upon completion. */
void grpc_setup_secure_transport(grpc_security_context *ctx,
void grpc_setup_secure_transport(grpc_security_connector *connector,
grpc_endpoint *nonsecure_endpoint,
grpc_secure_transport_setup_done_cb cb,
void *user_data);

@ -31,12 +31,10 @@
*
*/
#include "src/core/security/security_context.h"
#include "src/core/security/security_connector.h"
#include <string.h>
#include "src/core/channel/channel_args.h"
#include "src/core/channel/http_client_filter.h"
#include "src/core/security/credentials.h"
#include "src/core/security/secure_endpoint.h"
#include "src/core/support/env.h"
@ -56,7 +54,8 @@
#ifndef INSTALL_PREFIX
static const char *installed_roots_path = "/usr/share/grpc/roots.pem";
#else
static const char *installed_roots_path = INSTALL_PREFIX "/share/grpc/roots.pem";
static const char *installed_roots_path =
INSTALL_PREFIX "/share/grpc/roots.pem";
#endif
/* -- Cipher suites. -- */
@ -82,75 +81,77 @@ static const char *ssl_cipher_suites(void) {
/* -- Common methods. -- */
grpc_security_status grpc_security_context_create_handshaker(
grpc_security_context *ctx, tsi_handshaker **handshaker) {
if (ctx == NULL || handshaker == NULL) return GRPC_SECURITY_ERROR;
return ctx->vtable->create_handshaker(ctx, handshaker);
grpc_security_status grpc_security_connector_create_handshaker(
grpc_security_connector *sc, tsi_handshaker **handshaker) {
if (sc == NULL || handshaker == NULL) return GRPC_SECURITY_ERROR;
return sc->vtable->create_handshaker(sc, handshaker);
}
grpc_security_status grpc_security_context_check_peer(
grpc_security_context *ctx, tsi_peer peer, grpc_security_check_cb cb,
grpc_security_status grpc_security_connector_check_peer(
grpc_security_connector *sc, tsi_peer peer, grpc_security_check_cb cb,
void *user_data) {
if (ctx == NULL) {
if (sc == NULL) {
tsi_peer_destruct(&peer);
return GRPC_SECURITY_ERROR;
}
return ctx->vtable->check_peer(ctx, peer, cb, user_data);
return sc->vtable->check_peer(sc, peer, cb, user_data);
}
grpc_security_status grpc_channel_security_context_check_call_host(
grpc_channel_security_context *ctx, const char *host,
grpc_security_status grpc_channel_security_connector_check_call_host(
grpc_channel_security_connector *sc, const char *host,
grpc_security_check_cb cb, void *user_data) {
if (ctx == NULL || ctx->check_call_host == NULL) return GRPC_SECURITY_ERROR;
return ctx->check_call_host(ctx, host, cb, user_data);
if (sc == NULL || sc->check_call_host == NULL) return GRPC_SECURITY_ERROR;
return sc->check_call_host(sc, host, cb, user_data);
}
void grpc_security_context_unref(grpc_security_context *ctx) {
if (ctx == NULL) return;
if (gpr_unref(&ctx->refcount)) ctx->vtable->destroy(ctx);
void grpc_security_connector_unref(grpc_security_connector *sc) {
if (sc == NULL) return;
if (gpr_unref(&sc->refcount)) sc->vtable->destroy(sc);
}
grpc_security_context *grpc_security_context_ref(grpc_security_context *ctx) {
if (ctx == NULL) return NULL;
gpr_ref(&ctx->refcount);
return ctx;
grpc_security_connector *grpc_security_connector_ref(
grpc_security_connector *sc) {
if (sc == NULL) return NULL;
gpr_ref(&sc->refcount);
return sc;
}
static void context_pointer_arg_destroy(void *p) {
grpc_security_context_unref(p);
static void connector_pointer_arg_destroy(void *p) {
grpc_security_connector_unref(p);
}
static void *context_pointer_arg_copy(void *p) {
return grpc_security_context_ref(p);
static void *connector_pointer_arg_copy(void *p) {
return grpc_security_connector_ref(p);
}
grpc_arg grpc_security_context_to_arg(grpc_security_context *ctx) {
grpc_arg grpc_security_connector_to_arg(grpc_security_connector *sc) {
grpc_arg result;
result.type = GRPC_ARG_POINTER;
result.key = GRPC_SECURITY_CONTEXT_ARG;
result.value.pointer.destroy = context_pointer_arg_destroy;
result.value.pointer.copy = context_pointer_arg_copy;
result.value.pointer.p = ctx;
result.key = GRPC_SECURITY_CONNECTOR_ARG;
result.value.pointer.destroy = connector_pointer_arg_destroy;
result.value.pointer.copy = connector_pointer_arg_copy;
result.value.pointer.p = sc;
return result;
}
grpc_security_context *grpc_security_context_from_arg(const grpc_arg *arg) {
if (strcmp(arg->key, GRPC_SECURITY_CONTEXT_ARG)) return NULL;
grpc_security_connector *grpc_security_connector_from_arg(const grpc_arg *arg) {
if (strcmp(arg->key, GRPC_SECURITY_CONNECTOR_ARG)) return NULL;
if (arg->type != GRPC_ARG_POINTER) {
gpr_log(GPR_ERROR, "Invalid type %d for arg %s", arg->type,
GRPC_SECURITY_CONTEXT_ARG);
GRPC_SECURITY_CONNECTOR_ARG);
return NULL;
}
return arg->value.pointer.p;
}
grpc_security_context *grpc_find_security_context_in_args(
grpc_security_connector *grpc_find_security_connector_in_args(
const grpc_channel_args *args) {
size_t i;
if (args == NULL) return NULL;
for (i = 0; i < args->num_args; i++) {
grpc_security_context *ctx = grpc_security_context_from_arg(&args->args[i]);
if (ctx != NULL) return ctx;
grpc_security_connector *sc =
grpc_security_connector_from_arg(&args->args[i]);
if (sc != NULL) return sc;
}
return NULL;
}
@ -158,7 +159,7 @@ grpc_security_context *grpc_find_security_context_in_args(
static int check_request_metadata_creds(grpc_credentials *creds) {
if (creds != NULL && !grpc_credentials_has_request_metadata(creds)) {
gpr_log(GPR_ERROR,
"Incompatible credentials for channel security context: needs to "
"Incompatible credentials for channel security connector: needs to "
"set request metadata.");
return 0;
}
@ -168,31 +169,31 @@ static int check_request_metadata_creds(grpc_credentials *creds) {
/* -- Fake implementation. -- */
typedef struct {
grpc_channel_security_context base;
grpc_channel_security_connector base;
int call_host_check_is_async;
} grpc_fake_channel_security_context;
} grpc_fake_channel_security_connector;
static void fake_channel_destroy(grpc_security_context *ctx) {
grpc_channel_security_context *c = (grpc_channel_security_context *)ctx;
static void fake_channel_destroy(grpc_security_connector *sc) {
grpc_channel_security_connector *c = (grpc_channel_security_connector *)sc;
grpc_credentials_unref(c->request_metadata_creds);
gpr_free(ctx);
gpr_free(sc);
}
static void fake_server_destroy(grpc_security_context *ctx) { gpr_free(ctx); }
static void fake_server_destroy(grpc_security_connector *sc) { gpr_free(sc); }
static grpc_security_status fake_channel_create_handshaker(
grpc_security_context *ctx, tsi_handshaker **handshaker) {
grpc_security_connector *sc, tsi_handshaker **handshaker) {
*handshaker = tsi_create_fake_handshaker(1);
return GRPC_SECURITY_OK;
}
static grpc_security_status fake_server_create_handshaker(
grpc_security_context *ctx, tsi_handshaker **handshaker) {
grpc_security_connector *sc, tsi_handshaker **handshaker) {
*handshaker = tsi_create_fake_handshaker(0);
return GRPC_SECURITY_OK;
}
static grpc_security_status fake_check_peer(grpc_security_context *ctx,
static grpc_security_status fake_check_peer(grpc_security_connector *sc,
tsi_peer peer,
grpc_security_check_cb cb,
void *user_data) {
@ -228,10 +229,10 @@ end:
}
static grpc_security_status fake_channel_check_call_host(
grpc_channel_security_context *ctx, const char *host,
grpc_channel_security_connector *sc, const char *host,
grpc_security_check_cb cb, void *user_data) {
grpc_fake_channel_security_context *c =
(grpc_fake_channel_security_context *)ctx;
grpc_fake_channel_security_connector *c =
(grpc_fake_channel_security_connector *)sc;
if (c->call_host_check_is_async) {
cb(user_data, GRPC_SECURITY_OK);
return GRPC_SECURITY_PENDING;
@ -240,16 +241,16 @@ static grpc_security_status fake_channel_check_call_host(
}
}
static grpc_security_context_vtable fake_channel_vtable = {
static grpc_security_connector_vtable fake_channel_vtable = {
fake_channel_destroy, fake_channel_create_handshaker, fake_check_peer};
static grpc_security_context_vtable fake_server_vtable = {
static grpc_security_connector_vtable fake_server_vtable = {
fake_server_destroy, fake_server_create_handshaker, fake_check_peer};
grpc_channel_security_context *grpc_fake_channel_security_context_create(
grpc_channel_security_connector *grpc_fake_channel_security_connector_create(
grpc_credentials *request_metadata_creds, int call_host_check_is_async) {
grpc_fake_channel_security_context *c =
gpr_malloc(sizeof(grpc_fake_channel_security_context));
grpc_fake_channel_security_connector *c =
gpr_malloc(sizeof(grpc_fake_channel_security_connector));
gpr_ref_init(&c->base.base.refcount, 1);
c->base.base.is_client_side = 1;
c->base.base.url_scheme = GRPC_FAKE_SECURITY_URL_SCHEME;
@ -261,8 +262,8 @@ grpc_channel_security_context *grpc_fake_channel_security_context_create(
return &c->base;
}
grpc_security_context *grpc_fake_server_security_context_create(void) {
grpc_security_context *c = gpr_malloc(sizeof(grpc_security_context));
grpc_security_connector *grpc_fake_server_security_connector_create(void) {
grpc_security_connector *c = gpr_malloc(sizeof(grpc_security_connector));
gpr_ref_init(&c->refcount, 1);
c->vtable = &fake_server_vtable;
c->url_scheme = GRPC_FAKE_SECURITY_URL_SCHEME;
@ -272,21 +273,21 @@ grpc_security_context *grpc_fake_server_security_context_create(void) {
/* --- Ssl implementation. --- */
typedef struct {
grpc_channel_security_context base;
grpc_channel_security_connector base;
tsi_ssl_handshaker_factory *handshaker_factory;
char *target_name;
char *overridden_target_name;
tsi_peer peer;
} grpc_ssl_channel_security_context;
} grpc_ssl_channel_security_connector;
typedef struct {
grpc_security_context base;
grpc_security_connector base;
tsi_ssl_handshaker_factory *handshaker_factory;
} grpc_ssl_server_security_context;
} grpc_ssl_server_security_connector;
static void ssl_channel_destroy(grpc_security_context *ctx) {
grpc_ssl_channel_security_context *c =
(grpc_ssl_channel_security_context *)ctx;
static void ssl_channel_destroy(grpc_security_connector *sc) {
grpc_ssl_channel_security_connector *c =
(grpc_ssl_channel_security_connector *)sc;
grpc_credentials_unref(c->base.request_metadata_creds);
if (c->handshaker_factory != NULL) {
tsi_ssl_handshaker_factory_destroy(c->handshaker_factory);
@ -294,15 +295,16 @@ static void ssl_channel_destroy(grpc_security_context *ctx) {
if (c->target_name != NULL) gpr_free(c->target_name);
if (c->overridden_target_name != NULL) gpr_free(c->overridden_target_name);
tsi_peer_destruct(&c->peer);
gpr_free(ctx);
gpr_free(sc);
}
static void ssl_server_destroy(grpc_security_context *ctx) {
grpc_ssl_server_security_context *c = (grpc_ssl_server_security_context *)ctx;
static void ssl_server_destroy(grpc_security_connector *sc) {
grpc_ssl_server_security_connector *c =
(grpc_ssl_server_security_connector *)sc;
if (c->handshaker_factory != NULL) {
tsi_ssl_handshaker_factory_destroy(c->handshaker_factory);
}
gpr_free(ctx);
gpr_free(sc);
}
static grpc_security_status ssl_create_handshaker(
@ -321,9 +323,9 @@ static grpc_security_status ssl_create_handshaker(
}
static grpc_security_status ssl_channel_create_handshaker(
grpc_security_context *ctx, tsi_handshaker **handshaker) {
grpc_ssl_channel_security_context *c =
(grpc_ssl_channel_security_context *)ctx;
grpc_security_connector *sc, tsi_handshaker **handshaker) {
grpc_ssl_channel_security_connector *c =
(grpc_ssl_channel_security_connector *)sc;
return ssl_create_handshaker(c->handshaker_factory, 1,
c->overridden_target_name != NULL
? c->overridden_target_name
@ -332,13 +334,13 @@ static grpc_security_status ssl_channel_create_handshaker(
}
static grpc_security_status ssl_server_create_handshaker(
grpc_security_context *ctx, tsi_handshaker **handshaker) {
grpc_ssl_server_security_context *c = (grpc_ssl_server_security_context *)ctx;
grpc_security_connector *sc, tsi_handshaker **handshaker) {
grpc_ssl_server_security_connector *c =
(grpc_ssl_server_security_connector *)sc;
return ssl_create_handshaker(c->handshaker_factory, 0, NULL, handshaker);
}
static int ssl_host_matches_name(const tsi_peer *peer,
const char *peer_name) {
static int ssl_host_matches_name(const tsi_peer *peer, const char *peer_name) {
char *allocated_name = NULL;
int r;
@ -349,7 +351,6 @@ static int ssl_host_matches_name(const tsi_peer *peer,
peer_name = allocated_name;
if (!peer_name) return 0;
}
r = tsi_ssl_peer_matches_name(peer, peer_name);
gpr_free(allocated_name);
return r;
@ -375,8 +376,7 @@ static grpc_security_status ssl_check_peer(const char *peer_name,
}
/* Check the peer name if specified. */
if (peer_name != NULL &&
!ssl_host_matches_name(peer, peer_name)) {
if (peer_name != NULL && !ssl_host_matches_name(peer, peer_name)) {
gpr_log(GPR_ERROR, "Peer name %s is not in peer certificate", peer_name);
return GRPC_SECURITY_ERROR;
}
@ -384,12 +384,12 @@ static grpc_security_status ssl_check_peer(const char *peer_name,
return GRPC_SECURITY_OK;
}
static grpc_security_status ssl_channel_check_peer(grpc_security_context *ctx,
static grpc_security_status ssl_channel_check_peer(grpc_security_connector *sc,
tsi_peer peer,
grpc_security_check_cb cb,
void *user_data) {
grpc_ssl_channel_security_context *c =
(grpc_ssl_channel_security_context *)ctx;
grpc_ssl_channel_security_connector *c =
(grpc_ssl_channel_security_connector *)sc;
grpc_security_status status;
tsi_peer_destruct(&c->peer);
c->peer = peer;
@ -400,7 +400,7 @@ static grpc_security_status ssl_channel_check_peer(grpc_security_context *ctx,
return status;
}
static grpc_security_status ssl_server_check_peer(grpc_security_context *ctx,
static grpc_security_status ssl_server_check_peer(grpc_security_connector *sc,
tsi_peer peer,
grpc_security_check_cb cb,
void *user_data) {
@ -411,10 +411,10 @@ static grpc_security_status ssl_server_check_peer(grpc_security_context *ctx,
}
static grpc_security_status ssl_channel_check_call_host(
grpc_channel_security_context *ctx, const char *host,
grpc_channel_security_connector *sc, const char *host,
grpc_security_check_cb cb, void *user_data) {
grpc_ssl_channel_security_context *c =
(grpc_ssl_channel_security_context *)ctx;
grpc_ssl_channel_security_connector *c =
(grpc_ssl_channel_security_connector *)sc;
if (ssl_host_matches_name(&c->peer, host)) return GRPC_SECURITY_OK;
@ -428,10 +428,10 @@ static grpc_security_status ssl_channel_check_call_host(
}
}
static grpc_security_context_vtable ssl_channel_vtable = {
static grpc_security_connector_vtable ssl_channel_vtable = {
ssl_channel_destroy, ssl_channel_create_handshaker, ssl_channel_check_peer};
static grpc_security_context_vtable ssl_server_vtable = {
static grpc_security_connector_vtable ssl_server_vtable = {
ssl_server_destroy, ssl_server_create_handshaker, ssl_server_check_peer};
static gpr_slice default_pem_root_certs;
@ -443,13 +443,13 @@ static void init_default_pem_root_certs(void) {
if (default_root_certs_path == NULL) {
default_pem_root_certs = gpr_empty_slice();
} else {
default_pem_root_certs = gpr_load_file(default_root_certs_path, NULL);
default_pem_root_certs = gpr_load_file(default_root_certs_path, 0, NULL);
gpr_free(default_root_certs_path);
}
/* Fall back to installed certs if needed. */
if (GPR_SLICE_IS_EMPTY(default_pem_root_certs)) {
default_pem_root_certs = gpr_load_file(installed_roots_path, NULL);
default_pem_root_certs = gpr_load_file(installed_roots_path, 0, NULL);
}
}
@ -462,17 +462,17 @@ size_t grpc_get_default_ssl_roots(const unsigned char **pem_root_certs) {
return GPR_SLICE_LENGTH(default_pem_root_certs);
}
grpc_security_status grpc_ssl_channel_security_context_create(
grpc_security_status grpc_ssl_channel_security_connector_create(
grpc_credentials *request_metadata_creds, const grpc_ssl_config *config,
const char *target_name, const char *overridden_target_name,
grpc_channel_security_context **ctx) {
grpc_channel_security_connector **sc) {
size_t num_alpn_protocols = grpc_chttp2_num_alpn_versions();
const unsigned char **alpn_protocol_strings =
gpr_malloc(sizeof(const char *) * num_alpn_protocols);
unsigned char *alpn_protocol_string_lengths =
gpr_malloc(sizeof(unsigned char) * num_alpn_protocols);
tsi_result result = TSI_OK;
grpc_ssl_channel_security_context *c;
grpc_ssl_channel_security_connector *c;
size_t i;
const unsigned char *pem_root_certs;
size_t pem_root_certs_size;
@ -493,8 +493,8 @@ grpc_security_status grpc_ssl_channel_security_context_create(
goto error;
}
c = gpr_malloc(sizeof(grpc_ssl_channel_security_context));
memset(c, 0, sizeof(grpc_ssl_channel_security_context));
c = gpr_malloc(sizeof(grpc_ssl_channel_security_connector));
memset(c, 0, sizeof(grpc_ssl_channel_security_connector));
gpr_ref_init(&c->base.base.refcount, 1);
c->base.base.vtable = &ssl_channel_vtable;
@ -526,10 +526,10 @@ grpc_security_status grpc_ssl_channel_security_context_create(
gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.",
tsi_result_to_string(result));
ssl_channel_destroy(&c->base.base);
*ctx = NULL;
*sc = NULL;
goto error;
}
*ctx = &c->base;
*sc = &c->base;
gpr_free(alpn_protocol_strings);
gpr_free(alpn_protocol_string_lengths);
return GRPC_SECURITY_OK;
@ -540,15 +540,15 @@ error:
return GRPC_SECURITY_ERROR;
}
grpc_security_status grpc_ssl_server_security_context_create(
const grpc_ssl_server_config *config, grpc_security_context **ctx) {
grpc_security_status grpc_ssl_server_security_connector_create(
const grpc_ssl_server_config *config, grpc_security_connector **sc) {
size_t num_alpn_protocols = grpc_chttp2_num_alpn_versions();
const unsigned char **alpn_protocol_strings =
gpr_malloc(sizeof(const char *) * num_alpn_protocols);
unsigned char *alpn_protocol_string_lengths =
gpr_malloc(sizeof(unsigned char) * num_alpn_protocols);
tsi_result result = TSI_OK;
grpc_ssl_server_security_context *c;
grpc_ssl_server_security_connector *c;
size_t i;
for (i = 0; i < num_alpn_protocols; i++) {
@ -562,8 +562,8 @@ grpc_security_status grpc_ssl_server_security_context_create(
gpr_log(GPR_ERROR, "An SSL server needs a key and a cert.");
goto error;
}
c = gpr_malloc(sizeof(grpc_ssl_server_security_context));
memset(c, 0, sizeof(grpc_ssl_server_security_context));
c = gpr_malloc(sizeof(grpc_ssl_server_security_connector));
memset(c, 0, sizeof(grpc_ssl_server_security_connector));
gpr_ref_init(&c->base.refcount, 1);
c->base.url_scheme = GRPC_SSL_URL_SCHEME;
@ -573,17 +573,17 @@ grpc_security_status grpc_ssl_server_security_context_create(
config->pem_private_keys_sizes,
(const unsigned char **)config->pem_cert_chains,
config->pem_cert_chains_sizes, config->num_key_cert_pairs,
config->pem_root_certs, config->pem_root_certs_size,
ssl_cipher_suites(), alpn_protocol_strings,
alpn_protocol_string_lengths, num_alpn_protocols, &c->handshaker_factory);
config->pem_root_certs, config->pem_root_certs_size, ssl_cipher_suites(),
alpn_protocol_strings, alpn_protocol_string_lengths, num_alpn_protocols,
&c->handshaker_factory);
if (result != TSI_OK) {
gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.",
tsi_result_to_string(result));
ssl_server_destroy(&c->base);
*ctx = NULL;
*sc = NULL;
goto error;
}
*ctx = &c->base;
*sc = &c->base;
gpr_free(alpn_protocol_strings);
gpr_free(alpn_protocol_string_lengths);
return GRPC_SECURITY_OK;
@ -594,83 +594,3 @@ error:
return GRPC_SECURITY_ERROR;
}
/* -- High level objects. -- */
grpc_channel *grpc_ssl_channel_create(grpc_credentials *ssl_creds,
grpc_credentials *request_metadata_creds,
const char *target,
const grpc_channel_args *args) {
grpc_channel_security_context *ctx = NULL;
grpc_channel *channel = NULL;
grpc_security_status status = GRPC_SECURITY_OK;
size_t i = 0;
const char *overridden_target_name = NULL;
grpc_arg arg;
grpc_channel_args *new_args;
for (i = 0; args && i < args->num_args; i++) {
grpc_arg *arg = &args->args[i];
if (strcmp(arg->key, GRPC_SSL_TARGET_NAME_OVERRIDE_ARG) == 0 &&
arg->type == GRPC_ARG_STRING) {
overridden_target_name = arg->value.string;
break;
}
}
status = grpc_ssl_channel_security_context_create(
request_metadata_creds, grpc_ssl_credentials_get_config(ssl_creds),
target, overridden_target_name, &ctx);
if (status != GRPC_SECURITY_OK) {
return grpc_lame_client_channel_create();
}
arg.type = GRPC_ARG_STRING;
arg.key = GRPC_ARG_HTTP2_SCHEME;
arg.value.string = "https";
new_args = grpc_channel_args_copy_and_add(args, &arg);
channel = grpc_secure_channel_create_internal(target, new_args, ctx);
grpc_security_context_unref(&ctx->base);
grpc_channel_args_destroy(new_args);
return channel;
}
grpc_channel *grpc_fake_transport_security_channel_create(
grpc_credentials *fake_creds, grpc_credentials *request_metadata_creds,
const char *target, const grpc_channel_args *args) {
grpc_channel_security_context *ctx =
grpc_fake_channel_security_context_create(request_metadata_creds, 1);
grpc_channel *channel =
grpc_secure_channel_create_internal(target, args, ctx);
grpc_security_context_unref(&ctx->base);
return channel;
}
grpc_channel *grpc_secure_channel_create_with_factories(
const grpc_secure_channel_factory *factories, size_t num_factories,
grpc_credentials *creds, const char *target,
const grpc_channel_args *args) {
size_t i;
if (creds == NULL) {
gpr_log(GPR_ERROR, "No credentials to create a secure channel.");
return grpc_lame_client_channel_create();
}
if (grpc_credentials_has_request_metadata_only(creds)) {
gpr_log(GPR_ERROR,
"Credentials is insufficient to create a secure channel.");
return grpc_lame_client_channel_create();
}
for (i = 0; i < num_factories; i++) {
grpc_credentials *composite_creds = NULL;
grpc_credentials *transport_security_creds = NULL;
transport_security_creds = grpc_credentials_contains_type(
creds, factories[i].creds_type, &composite_creds);
if (transport_security_creds != NULL) {
return factories[i].factory(transport_security_creds, composite_creds,
target, args);
}
}
gpr_log(GPR_ERROR,
"Unknown credentials type %s for creating a secure channel.",
creds->type);
return grpc_lame_client_channel_create();
}

@ -0,0 +1,201 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef GRPC_INTERNAL_CORE_SECURITY_SECURITY_CONNECTOR_H
#define GRPC_INTERNAL_CORE_SECURITY_SECURITY_CONNECTOR_H
#include <grpc/grpc_security.h>
#include "src/core/iomgr/endpoint.h"
#include "src/core/tsi/transport_security_interface.h"
/* --- status enum. --- */
typedef enum {
GRPC_SECURITY_OK = 0,
GRPC_SECURITY_PENDING,
GRPC_SECURITY_ERROR
} grpc_security_status;
/* --- URL schemes. --- */
#define GRPC_SSL_URL_SCHEME "https"
#define GRPC_FAKE_SECURITY_URL_SCHEME "http+fake_security"
/* --- security_connector object. ---
A security connector object represents away to configure the underlying
transport security mechanism and check the resulting trusted peer. */
typedef struct grpc_security_connector grpc_security_connector;
#define GRPC_SECURITY_CONNECTOR_ARG "grpc.security_connector"
typedef void (*grpc_security_check_cb)(void *user_data,
grpc_security_status status);
typedef struct {
void (*destroy)(grpc_security_connector *sc);
grpc_security_status (*create_handshaker)(grpc_security_connector *sc,
tsi_handshaker **handshaker);
grpc_security_status (*check_peer)(grpc_security_connector *sc, tsi_peer peer,
grpc_security_check_cb cb,
void *user_data);
} grpc_security_connector_vtable;
struct grpc_security_connector {
const grpc_security_connector_vtable *vtable;
gpr_refcount refcount;
int is_client_side;
const char *url_scheme;
};
/* Increments the refcount. */
grpc_security_connector *grpc_security_connector_ref(
grpc_security_connector *sc);
/* Decrements the refcount and destroys the object if it reaches 0. */
void grpc_security_connector_unref(grpc_security_connector *sc);
/* Handshake creation. */
grpc_security_status grpc_security_connector_create_handshaker(
grpc_security_connector *sc, tsi_handshaker **handshaker);
/* Check the peer.
Implementations can choose to check the peer either synchronously or
asynchronously. In the first case, a successful call will return
GRPC_SECURITY_OK. In the asynchronous case, the call will return
GRPC_SECURITY_PENDING unless an error is detected early on.
Ownership of the peer is transfered.
*/
grpc_security_status grpc_security_connector_check_peer(
grpc_security_connector *sc, tsi_peer peer, grpc_security_check_cb cb,
void *user_data);
/* Util to encapsulate the connector in a channel arg. */
grpc_arg grpc_security_connector_to_arg(grpc_security_connector *sc);
/* Util to get the connector from a channel arg. */
grpc_security_connector *grpc_security_connector_from_arg(const grpc_arg *arg);
/* Util to find the connector from channel args. */
grpc_security_connector *grpc_find_security_connector_in_args(
const grpc_channel_args *args);
/* --- channel_security_connector object. ---
A channel security connector object represents away to configure the
underlying transport security mechanism on the client side. */
typedef struct grpc_channel_security_connector grpc_channel_security_connector;
struct grpc_channel_security_connector {
grpc_security_connector base; /* requires is_client_side to be non 0. */
grpc_credentials *request_metadata_creds;
grpc_security_status (*check_call_host)(grpc_channel_security_connector *sc,
const char *host,
grpc_security_check_cb cb,
void *user_data);
};
/* Checks that the host that will be set for a call is acceptable.
Implementations can choose do the check either synchronously or
asynchronously. In the first case, a successful call will return
GRPC_SECURITY_OK. In the asynchronous case, the call will return
GRPC_SECURITY_PENDING unless an error is detected early on. */
grpc_security_status grpc_channel_security_connector_check_call_host(
grpc_channel_security_connector *sc, const char *host,
grpc_security_check_cb cb, void *user_data);
/* --- Creation security connectors. --- */
/* For TESTING ONLY!
Creates a fake connector that emulates real channel security. */
grpc_channel_security_connector *grpc_fake_channel_security_connector_create(
grpc_credentials *request_metadata_creds, int call_host_check_is_async);
/* For TESTING ONLY!
Creates a fake connector that emulates real server security. */
grpc_security_connector *grpc_fake_server_security_connector_create(void);
/* Config for ssl clients. */
typedef struct {
unsigned char *pem_private_key;
size_t pem_private_key_size;
unsigned char *pem_cert_chain;
size_t pem_cert_chain_size;
unsigned char *pem_root_certs;
size_t pem_root_certs_size;
} grpc_ssl_config;
/* Creates an SSL channel_security_connector.
- request_metadata_creds is the credentials object which metadata
will be sent with each request. This parameter can be NULL.
- config is the SSL config to be used for the SSL channel establishment.
- is_client should be 0 for a server or a non-0 value for a client.
- secure_peer_name is the secure peer name that should be checked in
grpc_channel_security_connector_check_peer. This parameter may be NULL in
which case the peer name will not be checked. Note that if this parameter
is not NULL, then, pem_root_certs should not be NULL either.
- sc is a pointer on the connector to be created.
This function returns GRPC_SECURITY_OK in case of success or a
specific error code otherwise.
*/
grpc_security_status grpc_ssl_channel_security_connector_create(
grpc_credentials *request_metadata_creds,
const grpc_ssl_config *config, const char *target_name,
const char *overridden_target_name, grpc_channel_security_connector **sc);
/* Gets the default ssl roots. */
size_t grpc_get_default_ssl_roots(const unsigned char **pem_root_certs);
/* Config for ssl servers. */
typedef struct {
unsigned char **pem_private_keys;
size_t *pem_private_keys_sizes;
unsigned char **pem_cert_chains;
size_t *pem_cert_chains_sizes;
size_t num_key_cert_pairs;
unsigned char *pem_root_certs;
size_t pem_root_certs_size;
} grpc_ssl_server_config;
/* Creates an SSL server_security_connector.
- config is the SSL config to be used for the SSL channel establishment.
- sc is a pointer on the connector to be created.
This function returns GRPC_SECURITY_OK in case of success or a
specific error code otherwise.
*/
grpc_security_status grpc_ssl_server_security_connector_create(
const grpc_ssl_server_config *config, grpc_security_connector **sc);
#endif /* GRPC_INTERNAL_CORE_SECURITY_SECURITY_CONNECTOR_H */

@ -1,215 +0,0 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef GRPC_INTERNAL_CORE_SECURITY_SECURITY_CONTEXT_H
#define GRPC_INTERNAL_CORE_SECURITY_SECURITY_CONTEXT_H
#include <grpc/grpc_security.h>
#include "src/core/iomgr/endpoint.h"
#include "src/core/security/credentials.h"
#include "src/core/tsi/transport_security_interface.h"
/* --- status enum. --- */
typedef enum {
GRPC_SECURITY_OK = 0,
GRPC_SECURITY_PENDING,
GRPC_SECURITY_ERROR
} grpc_security_status;
/* --- URL schemes. --- */
#define GRPC_SSL_URL_SCHEME "https"
#define GRPC_FAKE_SECURITY_URL_SCHEME "http+fake_security"
/* --- security_context object. ---
A security context object represents away to configure the underlying
transport security mechanism and check the resulting trusted peer. */
typedef struct grpc_security_context grpc_security_context;
#define GRPC_SECURITY_CONTEXT_ARG "grpc.security_context"
typedef void (*grpc_security_check_cb)(void *user_data,
grpc_security_status status);
typedef struct {
void (*destroy)(grpc_security_context *ctx);
grpc_security_status (*create_handshaker)(grpc_security_context *ctx,
tsi_handshaker **handshaker);
grpc_security_status (*check_peer)(grpc_security_context *ctx, tsi_peer peer,
grpc_security_check_cb cb,
void *user_data);
} grpc_security_context_vtable;
struct grpc_security_context {
const grpc_security_context_vtable *vtable;
gpr_refcount refcount;
int is_client_side;
const char *url_scheme;
};
/* Increments the refcount. */
grpc_security_context *grpc_security_context_ref(grpc_security_context *ctx);
/* Decrements the refcount and destroys the object if it reaches 0. */
void grpc_security_context_unref(grpc_security_context *ctx);
/* Handshake creation. */
grpc_security_status grpc_security_context_create_handshaker(
grpc_security_context *ctx, tsi_handshaker **handshaker);
/* Check the peer.
Implementations can choose to check the peer either synchronously or
asynchronously. In the first case, a successful call will return
GRPC_SECURITY_OK. In the asynchronous case, the call will return
GRPC_SECURITY_PENDING unless an error is detected early on.
Ownership of the peer is transfered.
*/
grpc_security_status grpc_security_context_check_peer(
grpc_security_context *ctx, tsi_peer peer,
grpc_security_check_cb cb, void *user_data);
/* Util to encapsulate the context in a channel arg. */
grpc_arg grpc_security_context_to_arg(grpc_security_context *ctx);
/* Util to get the context from a channel arg. */
grpc_security_context *grpc_security_context_from_arg(const grpc_arg *arg);
/* Util to find the context from channel args. */
grpc_security_context *grpc_find_security_context_in_args(
const grpc_channel_args *args);
/* --- channel_security_context object. ---
A channel security context object represents away to configure the
underlying transport security mechanism on the client side. */
typedef struct grpc_channel_security_context grpc_channel_security_context;
struct grpc_channel_security_context {
grpc_security_context base; /* requires is_client_side to be non 0. */
grpc_credentials *request_metadata_creds;
grpc_security_status (*check_call_host)(
grpc_channel_security_context *ctx, const char *host,
grpc_security_check_cb cb, void *user_data);
};
/* Checks that the host that will be set for a call is acceptable.
Implementations can choose do the check either synchronously or
asynchronously. In the first case, a successful call will return
GRPC_SECURITY_OK. In the asynchronous case, the call will return
GRPC_SECURITY_PENDING unless an error is detected early on. */
grpc_security_status grpc_channel_security_context_check_call_host(
grpc_channel_security_context *ctx, const char *host,
grpc_security_check_cb cb, void *user_data);
/* --- Creation security contexts. --- */
/* For TESTING ONLY!
Creates a fake context that emulates real channel security. */
grpc_channel_security_context *grpc_fake_channel_security_context_create(
grpc_credentials *request_metadata_creds, int call_host_check_is_async);
/* For TESTING ONLY!
Creates a fake context that emulates real server security. */
grpc_security_context *grpc_fake_server_security_context_create(void);
/* Creates an SSL channel_security_context.
- request_metadata_creds is the credentials object which metadata
will be sent with each request. This parameter can be NULL.
- config is the SSL config to be used for the SSL channel establishment.
- is_client should be 0 for a server or a non-0 value for a client.
- secure_peer_name is the secure peer name that should be checked in
grpc_channel_security_context_check_peer. This parameter may be NULL in
which case the peer name will not be checked. Note that if this parameter
is not NULL, then, pem_root_certs should not be NULL either.
- ctx is a pointer on the context to be created.
This function returns GRPC_SECURITY_OK in case of success or a
specific error code otherwise.
*/
grpc_security_status grpc_ssl_channel_security_context_create(
grpc_credentials *request_metadata_creds, const grpc_ssl_config *config,
const char *target_name, const char *overridden_target_name,
grpc_channel_security_context **ctx);
/* Creates an SSL server_security_context.
- config is the SSL config to be used for the SSL channel establishment.
- ctx is a pointer on the context to be created.
This function returns GRPC_SECURITY_OK in case of success or a
specific error code otherwise.
*/
grpc_security_status grpc_ssl_server_security_context_create(
const grpc_ssl_server_config *config, grpc_security_context **ctx);
/* --- Creation of high level objects. --- */
/* Secure client channel creation. */
size_t grpc_get_default_ssl_roots(const unsigned char **pem_root_certs);
grpc_channel *grpc_ssl_channel_create(grpc_credentials *ssl_creds,
grpc_credentials *request_metadata_creds,
const char *target,
const grpc_channel_args *args);
grpc_channel *grpc_fake_transport_security_channel_create(
grpc_credentials *fake_creds, grpc_credentials *request_metadata_creds,
const char *target, const grpc_channel_args *args);
grpc_channel *grpc_secure_channel_create_internal(
const char *target, const grpc_channel_args *args,
grpc_channel_security_context *ctx);
typedef grpc_channel *(*grpc_secure_channel_factory_func)(
grpc_credentials *transport_security_creds,
grpc_credentials *request_metadata_creds, const char *target,
const grpc_channel_args *args);
typedef struct {
const char *creds_type;
grpc_secure_channel_factory_func factory;
} grpc_secure_channel_factory;
grpc_channel *grpc_secure_channel_create_with_factories(
const grpc_secure_channel_factory *factories, size_t num_factories,
grpc_credentials *creds, const char *target, const grpc_channel_args *args);
/* Secure server creation. */
grpc_server *grpc_secure_server_create_internal(grpc_completion_queue *cq,
const grpc_channel_args *args,
grpc_security_context *ctx);
#endif /* GRPC_INTERNAL_CORE_SECURITY_SECURITY_CONTEXT_H */

@ -40,7 +40,8 @@
#include "src/core/iomgr/endpoint.h"
#include "src/core/iomgr/resolve_address.h"
#include "src/core/iomgr/tcp_server.h"
#include "src/core/security/security_context.h"
#include "src/core/security/credentials.h"
#include "src/core/security/security_connector.h"
#include "src/core/security/secure_transport_setup.h"
#include "src/core/surface/server.h"
#include "src/core/transport/chttp2_transport.h"
@ -52,7 +53,7 @@
typedef struct grpc_server_secure_state {
grpc_server *server;
grpc_tcp_server *tcp;
grpc_security_context *ctx;
grpc_security_connector *sc;
int is_shutdown;
gpr_mu mu;
gpr_refcount refcount;
@ -64,7 +65,7 @@ static void state_ref(grpc_server_secure_state *state) {
static void state_unref(grpc_server_secure_state *state) {
if (gpr_unref(&state->refcount)) {
grpc_security_context_unref(state->ctx);
grpc_security_connector_unref(state->sc);
gpr_free(state);
}
}
@ -104,7 +105,7 @@ static void on_secure_transport_setup_done(void *statep,
static void on_accept(void *statep, grpc_endpoint *tcp) {
grpc_server_secure_state *state = statep;
state_ref(state);
grpc_setup_secure_transport(state->ctx, tcp, on_secure_transport_setup_done,
grpc_setup_secure_transport(state->sc, tcp, on_secure_transport_setup_done,
state);
}
@ -137,20 +138,11 @@ int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr,
int port_num = -1;
int port_temp;
grpc_security_status status = GRPC_SECURITY_ERROR;
grpc_security_context *ctx = NULL;
grpc_security_connector *sc = NULL;
/* create security context */
if (creds == NULL) goto error;
if (strcmp(creds->type, GRPC_CREDENTIALS_TYPE_SSL) == 0) {
status = grpc_ssl_server_security_context_create(
grpc_ssl_server_credentials_get_config(creds), &ctx);
} else if (strcmp(creds->type,
GRPC_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY) == 0) {
ctx = grpc_fake_server_security_context_create();
status = GRPC_SECURITY_OK;
}
status = grpc_server_credentials_create_security_connector(creds, &sc);
if (status != GRPC_SECURITY_OK) {
gpr_log(GPR_ERROR,
"Unable to create secure server with credentials of type %s.",
@ -197,7 +189,7 @@ int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr,
state = gpr_malloc(sizeof(*state));
state->server = server;
state->tcp = tcp;
state->ctx = ctx;
state->sc = sc;
state->is_shutdown = 0;
gpr_mu_init(&state->mu);
gpr_ref_init(&state->refcount, 1);
@ -209,8 +201,8 @@ int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr,
/* Error path: cleanup and return */
error:
if (ctx) {
grpc_security_context_unref(ctx);
if (sc) {
grpc_security_connector_unref(sc);
}
if (resolved) {
grpc_resolved_addresses_destroy(resolved);

@ -41,13 +41,14 @@
#include "src/core/support/string.h"
gpr_slice gpr_load_file(const char *filename, int *success) {
gpr_slice gpr_load_file(const char *filename, int add_null_terminator,
int *success) {
unsigned char *contents = NULL;
size_t contents_size = 0;
unsigned char buf[4096];
char *error_msg = NULL;
gpr_slice result = gpr_empty_slice();
FILE *file = fopen(filename, "rb");
size_t bytes_read = 0;
if (file == NULL) {
gpr_asprintf(&error_msg, "Could not open file %s (error = %s).", filename,
@ -55,27 +56,22 @@ gpr_slice gpr_load_file(const char *filename, int *success) {
GPR_ASSERT(error_msg != NULL);
goto end;
}
while (1) {
size_t bytes_read = fread(buf, 1, sizeof(buf), file);
if (bytes_read > 0) {
contents = gpr_realloc(contents, contents_size + bytes_read);
memcpy(contents + contents_size, buf, bytes_read);
contents_size += bytes_read;
}
if (bytes_read < sizeof(buf)) {
if (ferror(file)) {
gpr_asprintf(&error_msg, "Error %s occured while reading file %s.",
strerror(errno), filename);
GPR_ASSERT(error_msg != NULL);
goto end;
} else {
GPR_ASSERT(feof(file));
break;
}
}
fseek(file, 0, SEEK_END);
contents_size = ftell(file);
fseek(file, 0, SEEK_SET);
contents = gpr_malloc(contents_size + (add_null_terminator ? 1 : 0));
bytes_read = fread(contents, 1, contents_size, file);
if (bytes_read < contents_size) {
GPR_ASSERT(ferror(file));
gpr_asprintf(&error_msg, "Error %s occured while reading file %s.",
strerror(errno), filename);
GPR_ASSERT(error_msg != NULL);
goto end;
}
if (success != NULL) *success = 1;
if (add_null_terminator) {
contents[contents_size++] = 0;
}
result = gpr_slice_new(contents, contents_size, gpr_free);
end:

@ -44,9 +44,11 @@ extern "C" {
/* File utility functions */
/* Loads the content of a file into a slice. The success parameter, if not NULL,
/* Loads the content of a file into a slice. add_null_terminator will add
a NULL terminator if non-zero. The success parameter, if not NULL,
will be set to 1 in case of success and 0 in case of failure. */
gpr_slice gpr_load_file(const char *filename, int *success);
gpr_slice gpr_load_file(const char *filename, int add_null_terminator,
int *success);
/* Creates a temporary file from a prefix.
If tmp_filename is not NULL, *tmp_filename is assigned the name of the

@ -76,7 +76,7 @@ static size_t bucket_for_unchecked(gpr_histogram *h, double x) {
/* bounds checked version of the above */
static size_t bucket_for(gpr_histogram *h, double x) {
size_t bucket = bucket_for_unchecked(h, GPR_CLAMP(x, 0, h->max_possible));
size_t bucket = bucket_for_unchecked(h, GPR_CLAMP(x, 1.0, h->max_possible));
GPR_ASSERT(bucket < h->num_buckets);
return bucket;
}

@ -38,21 +38,34 @@
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
/* initial allocation size (# of slices) */
#define INITIAL_CAPACITY 4
/* grow a buffer; requires INITIAL_CAPACITY > 1 */
/* grow a buffer; requires GRPC_SLICE_BUFFER_INLINE_ELEMENTS > 1 */
#define GROW(x) (3 * (x) / 2)
static void maybe_embiggen(gpr_slice_buffer *sb) {
if (sb->count == sb->capacity) {
sb->capacity = GROW(sb->capacity);
GPR_ASSERT(sb->capacity > sb->count);
if (sb->slices == sb->inlined) {
sb->slices = gpr_malloc(sb->capacity * sizeof(gpr_slice));
memcpy(sb->slices, sb->inlined, sb->count * sizeof(gpr_slice));
} else {
sb->slices = gpr_realloc(sb->slices, sb->capacity * sizeof(gpr_slice));
}
}
}
void gpr_slice_buffer_init(gpr_slice_buffer *sb) {
sb->count = 0;
sb->length = 0;
sb->capacity = INITIAL_CAPACITY;
sb->slices = gpr_malloc(sizeof(gpr_slice) * INITIAL_CAPACITY);
sb->capacity = GRPC_SLICE_BUFFER_INLINE_ELEMENTS;
sb->slices = sb->inlined;
}
void gpr_slice_buffer_destroy(gpr_slice_buffer *sb) {
gpr_slice_buffer_reset_and_unref(sb);
gpr_free(sb->slices);
if (sb->slices != sb->inlined) {
gpr_free(sb->slices);
}
}
gpr_uint8 *gpr_slice_buffer_tiny_add(gpr_slice_buffer *sb, unsigned n) {
@ -71,11 +84,7 @@ gpr_uint8 *gpr_slice_buffer_tiny_add(gpr_slice_buffer *sb, unsigned n) {
return out;
add_new:
if (sb->count == sb->capacity) {
sb->capacity = GROW(sb->capacity);
GPR_ASSERT(sb->capacity > sb->count);
sb->slices = gpr_realloc(sb->slices, sb->capacity * sizeof(gpr_slice));
}
maybe_embiggen(sb);
back = &sb->slices[sb->count];
sb->count++;
back->refcount = NULL;
@ -85,11 +94,7 @@ add_new:
size_t gpr_slice_buffer_add_indexed(gpr_slice_buffer *sb, gpr_slice s) {
size_t out = sb->count;
if (out == sb->capacity) {
sb->capacity = GROW(sb->capacity);
GPR_ASSERT(sb->capacity > sb->count);
sb->slices = gpr_realloc(sb->slices, sb->capacity * sizeof(gpr_slice));
}
maybe_embiggen(sb);
sb->slices[out] = s;
sb->length += GPR_SLICE_LENGTH(s);
sb->count = out + 1;
@ -116,12 +121,7 @@ void gpr_slice_buffer_add(gpr_slice_buffer *sb, gpr_slice s) {
memcpy(back->data.inlined.bytes + back->data.inlined.length,
s.data.inlined.bytes, cp1);
back->data.inlined.length = GPR_SLICE_INLINED_SIZE;
if (n == sb->capacity) {
sb->capacity = GROW(sb->capacity);
GPR_ASSERT(sb->capacity > sb->count);
sb->slices =
gpr_realloc(sb->slices, sb->capacity * sizeof(gpr_slice));
}
maybe_embiggen(sb);
back = &sb->slices[n];
sb->count = n + 1;
back->refcount = NULL;
@ -160,3 +160,16 @@ void gpr_slice_buffer_reset_and_unref(gpr_slice_buffer *sb) {
sb->count = 0;
sb->length = 0;
}
void gpr_slice_buffer_swap(gpr_slice_buffer *a, gpr_slice_buffer *b) {
gpr_slice_buffer temp = *a;
*a = *b;
*b = temp;
if (a->slices == b->inlined) {
a->slices = a->inlined;
}
if (b->slices == a->inlined) {
b->slices = b->inlined;
}
}

@ -39,6 +39,7 @@
#include <grpc/support/time.h>
#include <sys/timeb.h>
#include <windows.h>
gpr_timespec gpr_now(void) {
gpr_timespec now_tv;
@ -49,4 +50,23 @@ gpr_timespec gpr_now(void) {
return now_tv;
}
void gpr_sleep_until(gpr_timespec until) {
gpr_timespec now;
gpr_timespec delta;
DWORD sleep_millis;
for (;;) {
/* We could simplify by using clock_nanosleep instead, but it might be
* slightly less portable. */
now = gpr_now();
if (gpr_time_cmp(until, now) <= 0) {
return;
}
delta = gpr_time_sub(until, now);
sleep_millis = delta.tv_sec * GPR_MS_PER_SEC + delta.tv_nsec / GPR_NS_PER_MS;
Sleep(sleep_millis);
}
}
#endif /* GPR_WIN32 */

@ -33,7 +33,6 @@
#include "src/core/surface/call.h"
#include "src/core/channel/channel_stack.h"
#include "src/core/channel/metadata_buffer.h"
#include "src/core/iomgr/alarm.h"
#include "src/core/support/string.h"
#include "src/core/surface/byte_buffer_queue.h"
@ -41,6 +40,7 @@
#include "src/core/surface/completion_queue.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
@ -68,8 +68,10 @@ typedef struct {
} completed_request;
/* See request_set in grpc_call below for a description */
#define REQSET_EMPTY 255
#define REQSET_DONE 254
#define REQSET_EMPTY 'X'
#define REQSET_DONE 'Y'
#define MAX_SEND_INITIAL_METADATA_COUNT 3
typedef struct {
/* Overall status of the operation: starts OK, may degrade to
@ -92,6 +94,8 @@ typedef enum {
/* Status came from the application layer overriding whatever
the wire says */
STATUS_FROM_API_OVERRIDE = 0,
/* Status was created by some internal channel stack operation */
STATUS_FROM_CORE,
/* Status came from 'the wire' - or somewhere below the surface
layer */
STATUS_FROM_WIRE,
@ -204,12 +208,18 @@ struct grpc_call {
/* Call refcount - to keep the call alive during asynchronous operations */
gpr_refcount internal_refcount;
grpc_linked_mdelem send_initial_metadata[MAX_SEND_INITIAL_METADATA_COUNT];
grpc_linked_mdelem status_link;
grpc_linked_mdelem details_link;
size_t send_initial_metadata_count;
gpr_timespec send_deadline;
/* Data that the legacy api needs to track. To be deleted at some point
soon */
legacy_state *legacy_state;
};
#define CALL_STACK_FROM_CALL(call) ((grpc_call_stack *)((call)+1))
#define CALL_STACK_FROM_CALL(call) ((grpc_call_stack *)((call) + 1))
#define CALL_FROM_CALL_STACK(call_stack) (((grpc_call *)(call_stack)) - 1)
#define CALL_ELEM_FROM_CALL(call, idx) \
grpc_call_stack_element(CALL_STACK_FROM_CALL(call), idx)
@ -226,9 +236,13 @@ struct grpc_call {
static void do_nothing(void *ignored, grpc_op_error also_ignored) {}
static send_action choose_send_action(grpc_call *call);
static void enact_send_action(grpc_call *call, send_action sa);
static void set_deadline_alarm(grpc_call *call, gpr_timespec deadline);
grpc_call *grpc_call_create(grpc_channel *channel, grpc_completion_queue *cq,
const void *server_transport_data) {
const void *server_transport_data,
grpc_mdelem **add_initial_metadata,
size_t add_initial_metadata_count,
gpr_timespec send_deadline) {
size_t i;
grpc_channel_stack *channel_stack = grpc_channel_get_channel_stack(channel);
grpc_call *call =
@ -245,6 +259,12 @@ grpc_call *grpc_call_create(grpc_channel *channel, grpc_completion_queue *cq,
call->request_set[GRPC_IOREQ_SEND_TRAILING_METADATA] = REQSET_DONE;
call->request_set[GRPC_IOREQ_SEND_STATUS] = REQSET_DONE;
}
GPR_ASSERT(add_initial_metadata_count < MAX_SEND_INITIAL_METADATA_COUNT);
for (i = 0; i < add_initial_metadata_count; i++) {
call->send_initial_metadata[i].md = add_initial_metadata[i];
}
call->send_initial_metadata_count = add_initial_metadata_count;
call->send_deadline = send_deadline;
grpc_channel_internal_ref(channel);
call->metadata_context = grpc_channel_get_metadata_context(channel);
/* one ref is dropped in response to destroy, the other in
@ -252,6 +272,9 @@ grpc_call *grpc_call_create(grpc_channel *channel, grpc_completion_queue *cq,
gpr_ref_init(&call->internal_refcount, 2);
grpc_call_stack_init(channel_stack, server_transport_data,
CALL_STACK_FROM_CALL(call));
if (gpr_time_cmp(send_deadline, gpr_inf_future) != 0) {
set_deadline_alarm(call, send_deadline);
}
return call;
}
@ -284,6 +307,9 @@ static void destroy_call(void *call, int ignored_success) {
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);
}
if (c->legacy_state) {
destroy_legacy_state(c->legacy_state);
}
@ -342,6 +368,7 @@ static void request_more_data(grpc_call *call) {
op.flags = 0;
op.done_cb = do_nothing;
op.user_data = NULL;
op.bind_pollset = NULL;
grpc_call_execute_op(call, &op);
}
@ -587,15 +614,29 @@ static send_action choose_send_action(grpc_call *call) {
return SEND_NOTHING;
}
static void send_metadata(grpc_call *call, grpc_mdelem *elem) {
grpc_call_op op;
op.type = GRPC_SEND_METADATA;
op.dir = GRPC_CALL_DOWN;
op.flags = GRPC_WRITE_BUFFER_HINT;
op.data.metadata = elem;
op.done_cb = do_nothing;
op.user_data = NULL;
grpc_call_execute_op(call, &op);
static grpc_mdelem_list chain_metadata_from_app(grpc_call *call, size_t count,
grpc_metadata *metadata) {
size_t i;
grpc_mdelem_list out;
if (count == 0) {
out.head = out.tail = NULL;
return out;
}
for (i = 0; i < count; i++) {
grpc_metadata *md = &metadata[i];
grpc_metadata *next_md = (i == count - 1) ? NULL : &metadata[i + 1];
grpc_metadata *prev_md = (i == 0) ? NULL : &metadata[i - 1];
grpc_linked_mdelem *l = (grpc_linked_mdelem *)&md->internal_data;
GPR_ASSERT(sizeof(grpc_linked_mdelem) == sizeof(md->internal_data));
l->md = grpc_mdelem_from_string_and_buffer(call->metadata_context, md->key,
(const gpr_uint8 *)md->value,
md->value_length);
l->next = next_md ? (grpc_linked_mdelem *)&next_md->internal_data : NULL;
l->prev = prev_md ? (grpc_linked_mdelem *)&prev_md->internal_data : NULL;
}
out.head = (grpc_linked_mdelem *)&(metadata[0].internal_data);
out.tail = (grpc_linked_mdelem *)&(metadata[count - 1].internal_data);
return out;
}
static void enact_send_action(grpc_call *call, send_action sa) {
@ -614,19 +655,21 @@ static void enact_send_action(grpc_call *call, send_action sa) {
/* fallthrough */
case SEND_INITIAL_METADATA:
data = call->request_data[GRPC_IOREQ_SEND_INITIAL_METADATA];
for (i = 0; i < data.send_metadata.count; i++) {
const grpc_metadata *md = &data.send_metadata.metadata[i];
send_metadata(call,
grpc_mdelem_from_string_and_buffer(
call->metadata_context, md->key,
(const gpr_uint8 *)md->value, md->value_length));
}
op.type = GRPC_SEND_START;
op.type = GRPC_SEND_METADATA;
op.dir = GRPC_CALL_DOWN;
op.flags = flags;
op.data.start.pollset = grpc_cq_pollset(call->cq);
op.data.metadata.list = chain_metadata_from_app(
call, data.send_metadata.count, data.send_metadata.metadata);
op.data.metadata.garbage.head = op.data.metadata.garbage.tail = NULL;
op.data.metadata.deadline = call->send_deadline;
for (i = 0; i < call->send_initial_metadata_count; i++) {
grpc_metadata_batch_link_head(&op.data.metadata,
&call->send_initial_metadata[i]);
}
call->send_initial_metadata_count = 0;
op.done_cb = finish_start_step;
op.user_data = call;
op.bind_pollset = grpc_cq_pollset(call->cq);
grpc_call_execute_op(call, &op);
break;
case SEND_BUFFERED_MESSAGE:
@ -640,37 +683,42 @@ static void enact_send_action(grpc_call *call, send_action sa) {
op.data.message = data.send_message;
op.done_cb = finish_write_step;
op.user_data = call;
op.bind_pollset = NULL;
grpc_call_execute_op(call, &op);
break;
case SEND_TRAILING_METADATA_AND_FINISH:
/* send trailing metadata */
data = call->request_data[GRPC_IOREQ_SEND_TRAILING_METADATA];
for (i = 0; i < data.send_metadata.count; i++) {
const grpc_metadata *md = &data.send_metadata.metadata[i];
send_metadata(call,
grpc_mdelem_from_string_and_buffer(
call->metadata_context, md->key,
(const gpr_uint8 *)md->value, md->value_length));
}
op.type = GRPC_SEND_METADATA;
op.dir = GRPC_CALL_DOWN;
op.flags = flags;
op.data.metadata.list = chain_metadata_from_app(
call, data.send_metadata.count, data.send_metadata.metadata);
op.data.metadata.garbage.head = op.data.metadata.garbage.tail = NULL;
op.data.metadata.deadline = call->send_deadline;
op.bind_pollset = NULL;
/* send status */
/* TODO(ctiller): cache common status values */
data = call->request_data[GRPC_IOREQ_SEND_STATUS];
gpr_ltoa(data.send_status.code, status_str);
send_metadata(
call,
grpc_metadata_batch_add_tail(
&op.data.metadata, &call->status_link,
grpc_mdelem_from_metadata_strings(
call->metadata_context,
grpc_mdstr_ref(grpc_channel_get_status_string(call->channel)),
grpc_mdstr_from_string(call->metadata_context, status_str)));
if (data.send_status.details) {
send_metadata(
call,
grpc_metadata_batch_add_tail(
&op.data.metadata, &call->details_link,
grpc_mdelem_from_metadata_strings(
call->metadata_context,
grpc_mdstr_ref(grpc_channel_get_message_string(call->channel)),
grpc_mdstr_from_string(call->metadata_context,
data.send_status.details)));
}
op.done_cb = do_nothing;
op.user_data = NULL;
grpc_call_execute_op(call, &op);
/* fallthrough: see choose_send_action for details */
case SEND_FINISH:
op.type = GRPC_SEND_FINISH;
@ -678,6 +726,7 @@ static void enact_send_action(grpc_call *call, send_action sa) {
op.flags = 0;
op.done_cb = finish_finish_step;
op.user_data = call;
op.bind_pollset = NULL;
grpc_call_execute_op(call, &op);
break;
}
@ -831,6 +880,7 @@ grpc_call_error grpc_call_cancel(grpc_call *c) {
op.flags = 0;
op.done_cb = do_nothing;
op.user_data = NULL;
op.bind_pollset = NULL;
elem = CALL_ELEM_FROM_CALL(c, 0);
elem->filter->call_op(elem, NULL, &op);
@ -875,9 +925,7 @@ static void call_alarm(void *arg, int success) {
grpc_call_internal_unref(call, 1);
}
void grpc_call_set_deadline(grpc_call_element *elem, gpr_timespec deadline) {
grpc_call *call = CALL_FROM_TOP_ELEM(elem);
static void set_deadline_alarm(grpc_call *call, gpr_timespec deadline) {
if (call->have_alarm) {
gpr_log(GPR_ERROR, "Attempt to set deadline alarm twice");
}
@ -886,11 +934,15 @@ void grpc_call_set_deadline(grpc_call_element *elem, gpr_timespec deadline) {
grpc_alarm_init(&call->alarm, deadline, call_alarm, call, gpr_now());
}
static void set_read_state(grpc_call *call, read_state state) {
lock(call);
static void set_read_state_locked(grpc_call *call, read_state state) {
GPR_ASSERT(call->read_state < state);
call->read_state = state;
finish_read_ops(call);
}
static void set_read_state(grpc_call *call, read_state state) {
lock(call);
set_read_state_locked(call, state);
unlock(call);
}
@ -914,7 +966,7 @@ static gpr_uint32 decode_status(grpc_mdelem *md) {
gpr_uint32 status;
void *user_data = grpc_mdelem_get_user_data(md, destroy_status);
if (user_data) {
status = ((gpr_uint32)(gpr_intptr) user_data) - STATUS_OFFSET;
status = ((gpr_uint32)(gpr_intptr)user_data) - STATUS_OFFSET;
} else {
if (!gpr_parse_bytes_to_uint32(grpc_mdstr_as_c_string(md->value),
GPR_SLICE_LENGTH(md->value->slice),
@ -936,52 +988,81 @@ void grpc_call_recv_message(grpc_call_element *elem,
unlock(call);
}
void grpc_call_recv_metadata(grpc_call_element *elem, grpc_mdelem *md) {
void grpc_call_recv_synthetic_status(grpc_call_element *elem,
grpc_status_code status,
const char *message) {
grpc_call *call = CALL_FROM_TOP_ELEM(elem);
grpc_mdstr *key = md->key;
lock(call);
set_status_code(call, STATUS_FROM_CORE, status);
set_status_details(call, STATUS_FROM_CORE,
grpc_mdstr_from_string(call->metadata_context, message));
unlock(call);
}
int grpc_call_recv_metadata(grpc_call_element *elem, grpc_metadata_batch *md) {
grpc_call *call = CALL_FROM_TOP_ELEM(elem);
grpc_linked_mdelem *l;
grpc_metadata_array *dest;
grpc_metadata *mdusr;
int is_trailing;
grpc_mdctx *mdctx = call->metadata_context;
lock(call);
if (key == grpc_channel_get_status_string(call->channel)) {
set_status_code(call, STATUS_FROM_WIRE, decode_status(md));
grpc_mdelem_unref(md);
} else if (key == grpc_channel_get_message_string(call->channel)) {
set_status_details(call, STATUS_FROM_WIRE, grpc_mdstr_ref(md->value));
grpc_mdelem_unref(md);
} else {
dest = &call->buffered_metadata[call->read_state >=
READ_STATE_GOT_INITIAL_METADATA];
if (dest->count == dest->capacity) {
dest->capacity = GPR_MAX(dest->capacity + 8, dest->capacity * 2);
dest->metadata =
gpr_realloc(dest->metadata, sizeof(grpc_metadata) * dest->capacity);
}
mdusr = &dest->metadata[dest->count++];
mdusr->key = grpc_mdstr_as_c_string(md->key);
mdusr->value = grpc_mdstr_as_c_string(md->value);
mdusr->value_length = GPR_SLICE_LENGTH(md->value->slice);
if (call->owned_metadata_count == call->owned_metadata_capacity) {
call->owned_metadata_capacity = GPR_MAX(
call->owned_metadata_capacity + 8, call->owned_metadata_capacity * 2);
call->owned_metadata =
gpr_realloc(call->owned_metadata,
sizeof(grpc_mdelem *) * call->owned_metadata_capacity);
is_trailing = call->read_state >= READ_STATE_GOT_INITIAL_METADATA;
for (l = md->list.head; l != NULL; l = l->next) {
grpc_mdelem *md = l->md;
grpc_mdstr *key = md->key;
if (key == grpc_channel_get_status_string(call->channel)) {
set_status_code(call, STATUS_FROM_WIRE, decode_status(md));
} else if (key == grpc_channel_get_message_string(call->channel)) {
set_status_details(call, STATUS_FROM_WIRE, grpc_mdstr_ref(md->value));
} else {
dest = &call->buffered_metadata[is_trailing];
if (dest->count == dest->capacity) {
dest->capacity = GPR_MAX(dest->capacity + 8, dest->capacity * 2);
dest->metadata =
gpr_realloc(dest->metadata, sizeof(grpc_metadata) * dest->capacity);
}
mdusr = &dest->metadata[dest->count++];
mdusr->key = grpc_mdstr_as_c_string(md->key);
mdusr->value = grpc_mdstr_as_c_string(md->value);
mdusr->value_length = GPR_SLICE_LENGTH(md->value->slice);
if (call->owned_metadata_count == call->owned_metadata_capacity) {
call->owned_metadata_capacity =
GPR_MAX(call->owned_metadata_capacity + 8,
call->owned_metadata_capacity * 2);
call->owned_metadata =
gpr_realloc(call->owned_metadata,
sizeof(grpc_mdelem *) * call->owned_metadata_capacity);
}
call->owned_metadata[call->owned_metadata_count++] = md;
l->md = 0;
}
call->owned_metadata[call->owned_metadata_count++] = md;
}
if (gpr_time_cmp(md->deadline, gpr_inf_future) != 0) {
set_deadline_alarm(call, md->deadline);
}
if (!is_trailing) {
set_read_state_locked(call, READ_STATE_GOT_INITIAL_METADATA);
}
unlock(call);
grpc_mdctx_lock(mdctx);
for (l = md->list.head; l; l = l->next) {
if (l->md) grpc_mdctx_locked_mdelem_unref(mdctx, l->md);
}
for (l = md->garbage.head; l; l = l->next) {
grpc_mdctx_locked_mdelem_unref(mdctx, l->md);
}
grpc_mdctx_unlock(mdctx);
return !is_trailing;
}
grpc_call_stack *grpc_call_get_call_stack(grpc_call *call) {
return CALL_STACK_FROM_CALL(call);
}
void grpc_call_initial_metadata_complete(grpc_call_element *surface_element) {
grpc_call *call = grpc_call_from_top_element(surface_element);
set_read_state(call, READ_STATE_GOT_INITIAL_METADATA);
}
/*
* BATCH API IMPLEMENTATION
*/

@ -35,7 +35,6 @@
#define GRPC_INTERNAL_CORE_SURFACE_CALL_H
#include "src/core/channel/channel_stack.h"
#include "src/core/channel/metadata_buffer.h"
#include <grpc/grpc.h>
/* Primitive operation types - grpc_op's get rewritten into these */
@ -67,7 +66,7 @@ typedef union {
} recv_status_details;
struct {
size_t count;
const grpc_metadata *metadata;
grpc_metadata *metadata;
} send_metadata;
grpc_byte_buffer *send_message;
struct {
@ -86,7 +85,10 @@ typedef void (*grpc_ioreq_completion_func)(grpc_call *call,
void *user_data);
grpc_call *grpc_call_create(grpc_channel *channel, grpc_completion_queue *cq,
const void *server_transport_data);
const void *server_transport_data,
grpc_mdelem **add_initial_metadata,
size_t add_initial_metadata_count,
gpr_timespec send_deadline);
void grpc_call_set_completion_queue(grpc_call *call, grpc_completion_queue *cq);
grpc_completion_queue *grpc_call_get_completion_queue(grpc_call *call);
@ -96,8 +98,9 @@ void grpc_call_internal_unref(grpc_call *call, int allow_immediate_deletion);
/* Helpers for grpc_client, grpc_server filters to publish received data to
the completion queue/surface layer */
void grpc_call_recv_metadata(grpc_call_element *surface_element,
grpc_mdelem *md);
/* receive metadata - returns 1 if this was initial metadata */
int grpc_call_recv_metadata(grpc_call_element *surface_element,
grpc_metadata_batch *md);
void grpc_call_recv_message(grpc_call_element *surface_element,
grpc_byte_buffer *message);
void grpc_call_read_closed(grpc_call_element *surface_element);
@ -108,14 +111,12 @@ grpc_call_error grpc_call_start_ioreq_and_call_back(
grpc_call *call, const grpc_ioreq *reqs, size_t nreqs,
grpc_ioreq_completion_func on_complete, void *user_data);
/* Called when it's known that the initial batch of metadata is complete */
void grpc_call_initial_metadata_complete(grpc_call_element *surface_element);
void grpc_call_set_deadline(grpc_call_element *surface_element,
gpr_timespec deadline);
grpc_call_stack *grpc_call_get_call_stack(grpc_call *call);
void grpc_call_recv_synthetic_status(grpc_call_element *elem,
grpc_status_code status,
const char *message);
/* Given the top call_element, get the call object. */
grpc_call *grpc_call_from_top_element(grpc_call_element *surface_element);
@ -128,4 +129,4 @@ void grpc_call_log_batch(char *file, int line, gpr_log_severity severity,
#define GRPC_CALL_LOG_BATCH(sev, call, ops, nops, tag) \
if (grpc_trace_batch) grpc_call_log_batch(sev, call, ops, nops, tag)
#endif /* GRPC_INTERNAL_CORE_SURFACE_CALL_H */
#endif /* GRPC_INTERNAL_CORE_SURFACE_CALL_H */

@ -43,6 +43,12 @@
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
typedef struct registered_call {
grpc_mdelem *path;
grpc_mdelem *authority;
struct registered_call *next;
} registered_call;
struct grpc_channel {
int is_client;
gpr_refcount refs;
@ -51,10 +57,14 @@ struct grpc_channel {
grpc_mdstr *grpc_message_string;
grpc_mdstr *path_string;
grpc_mdstr *authority_string;
gpr_mu registered_call_mu;
registered_call *registered_calls;
};
#define CHANNEL_STACK_FROM_CHANNEL(c) ((grpc_channel_stack *)((c)+1))
#define CHANNEL_FROM_CHANNEL_STACK(channel_stack) (((grpc_channel *)(channel_stack)) - 1)
#define CHANNEL_STACK_FROM_CHANNEL(c) ((grpc_channel_stack *)((c) + 1))
#define CHANNEL_FROM_CHANNEL_STACK(channel_stack) \
(((grpc_channel *)(channel_stack)) - 1)
#define CHANNEL_FROM_TOP_ELEM(top_elem) \
CHANNEL_FROM_CHANNEL_STACK(grpc_channel_stack_from_top_element(top_elem))
@ -66,7 +76,8 @@ grpc_channel *grpc_channel_create_from_filters(
grpc_channel *channel = gpr_malloc(size);
GPR_ASSERT(grpc_is_initialized() && "call grpc_init()");
channel->is_client = is_client;
/* decremented by grpc_channel_destroy, and grpc_client_channel_closed if is_client */
/* decremented by grpc_channel_destroy, and grpc_client_channel_closed if
* is_client */
gpr_ref_init(&channel->refs, 1 + is_client);
channel->metadata_context = mdctx;
channel->grpc_status_string = grpc_mdstr_from_string(mdctx, "grpc-status");
@ -75,59 +86,23 @@ grpc_channel *grpc_channel_create_from_filters(
channel->authority_string = grpc_mdstr_from_string(mdctx, ":authority");
grpc_channel_stack_init(filters, num_filters, args, channel->metadata_context,
CHANNEL_STACK_FROM_CHANNEL(channel));
gpr_mu_init(&channel->registered_call_mu);
channel->registered_calls = NULL;
return channel;
}
static void do_nothing(void *ignored, grpc_op_error error) {}
static grpc_call *grpc_channel_create_call_internal(
grpc_channel *channel, grpc_completion_queue *cq, grpc_mdelem *path_mdelem,
grpc_mdelem *authority_mdelem, gpr_timespec deadline) {
grpc_mdelem *send_metadata[2];
grpc_call *grpc_channel_create_call(grpc_channel *channel,
grpc_completion_queue *cq,
const char *method, const char *host,
gpr_timespec absolute_deadline) {
grpc_call *call;
grpc_mdelem *path_mdelem;
grpc_mdelem *authority_mdelem;
grpc_call_op op;
if (!channel->is_client) {
gpr_log(GPR_ERROR, "Cannot create a call on the server.");
return NULL;
}
GPR_ASSERT(channel->is_client);
call = grpc_call_create(channel, cq, NULL);
send_metadata[0] = path_mdelem;
send_metadata[1] = authority_mdelem;
/* Add :path and :authority headers. */
/* TODO(klempner): Consider optimizing this by stashing mdelems for common
values of method and host. */
path_mdelem = grpc_mdelem_from_metadata_strings(
channel->metadata_context, grpc_mdstr_ref(channel->path_string),
grpc_mdstr_from_string(channel->metadata_context, method));
op.type = GRPC_SEND_METADATA;
op.dir = GRPC_CALL_DOWN;
op.flags = 0;
op.data.metadata = path_mdelem;
op.done_cb = do_nothing;
op.user_data = NULL;
grpc_call_execute_op(call, &op);
grpc_mdstr_ref(channel->authority_string);
authority_mdelem = grpc_mdelem_from_metadata_strings(
channel->metadata_context, channel->authority_string,
grpc_mdstr_from_string(channel->metadata_context, host));
op.data.metadata = authority_mdelem;
grpc_call_execute_op(call, &op);
if (0 != gpr_time_cmp(absolute_deadline, gpr_inf_future)) {
op.type = GRPC_SEND_DEADLINE;
op.dir = GRPC_CALL_DOWN;
op.flags = 0;
op.data.deadline = absolute_deadline;
op.done_cb = do_nothing;
op.user_data = NULL;
grpc_call_execute_op(call, &op);
}
return call;
return grpc_call_create(channel, cq, NULL, send_metadata,
GPR_ARRAY_SIZE(send_metadata), deadline);
}
grpc_call *grpc_channel_create_call_old(grpc_channel *channel,
@ -137,6 +112,46 @@ grpc_call *grpc_channel_create_call_old(grpc_channel *channel,
absolute_deadline);
}
grpc_call *grpc_channel_create_call(grpc_channel *channel,
grpc_completion_queue *cq,
const char *method, const char *host,
gpr_timespec deadline) {
return grpc_channel_create_call_internal(
channel, cq,
grpc_mdelem_from_metadata_strings(
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),
grpc_mdstr_from_string(channel->metadata_context, host)),
deadline);
}
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),
grpc_mdstr_from_string(channel->metadata_context, method));
rc->authority = grpc_mdelem_from_metadata_strings(
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;
channel->registered_calls = rc;
gpr_mu_unlock(&channel->registered_call_mu);
return rc;
}
grpc_call *grpc_channel_create_registered_call(
grpc_channel *channel, grpc_completion_queue *completion_queue,
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);
}
void grpc_channel_internal_ref(grpc_channel *channel) {
gpr_ref(&channel->refs);
}
@ -148,7 +163,15 @@ static void destroy_channel(void *p, int ok) {
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);
gpr_free(rc);
}
grpc_mdctx_unref(channel->metadata_context);
gpr_mu_destroy(&channel->registered_call_mu);
gpr_free(channel);
}

@ -39,28 +39,17 @@
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
typedef struct {
void *unused;
} call_data;
typedef struct { void *unused; } call_data;
typedef struct {
void *unused;
} channel_data;
typedef struct { void *unused; } channel_data;
static void call_op(grpc_call_element *elem, grpc_call_element *from_elem,
grpc_call_op *op) {
GRPC_CALL_LOG_OP(GPR_INFO, elem, op);
switch (op->type) {
case GRPC_SEND_DEADLINE:
grpc_call_set_deadline(elem, op->data.deadline);
grpc_call_next_op(elem, op);
break;
case GRPC_RECV_METADATA:
grpc_call_recv_metadata(elem, op->data.metadata);
break;
case GRPC_RECV_DEADLINE:
gpr_log(GPR_ERROR, "Deadline received by client (ignored)");
grpc_call_recv_metadata(elem, &op->data.metadata);
break;
case GRPC_RECV_MESSAGE:
grpc_call_recv_message(elem, op->data.message);
@ -72,8 +61,9 @@ static void call_op(grpc_call_element *elem, grpc_call_element *from_elem,
case GRPC_RECV_FINISH:
grpc_call_stream_closed(elem);
break;
case GRPC_RECV_END_OF_INITIAL_METADATA:
grpc_call_initial_metadata_complete(elem);
case GRPC_RECV_SYNTHETIC_STATUS:
grpc_call_recv_synthetic_status(elem, op->data.synthetic_status.status,
op->data.synthetic_status.message);
break;
default:
GPR_ASSERT(op->dir == GRPC_CALL_DOWN);
@ -114,6 +104,6 @@ static void init_channel_elem(grpc_channel_element *elem,
static void destroy_channel_elem(grpc_channel_element *elem) {}
const grpc_channel_filter grpc_client_surface_filter = {
call_op, channel_op, sizeof(call_data),
init_call_elem, destroy_call_elem, sizeof(channel_data),
init_channel_elem, destroy_channel_elem, "client", };
call_op, channel_op, sizeof(call_data), init_call_elem, destroy_call_elem,
sizeof(channel_data), init_channel_elem, destroy_channel_elem, "client",
};

@ -32,10 +32,11 @@
*/
#include <grpc/grpc.h>
#include "src/core/iomgr/iomgr.h"
#include "src/core/channel/channel_stack.h"
#include "src/core/debug/trace.h"
#include "src/core/iomgr/iomgr.h"
#include "src/core/statistics/census_interface.h"
#include "src/core/channel/channel_stack.h"
#include "src/core/profiling/timers.h"
#include "src/core/surface/call.h"
#include "src/core/surface/init.h"
#include "src/core/surface/surface_trace.h"
@ -60,9 +61,10 @@ void grpc_init(void) {
grpc_register_tracer("http", &grpc_http_trace);
grpc_register_tracer("batch", &grpc_trace_batch);
grpc_security_pre_init();
grpc_tracer_init("GRPC_TRACE");
grpc_iomgr_init();
grpc_tracer_init("GRPC_TRACE");
census_init();
grpc_timers_log_global_init();
}
gpr_mu_unlock(&g_init_mu);
}
@ -72,6 +74,7 @@ void grpc_shutdown(void) {
if (--g_initializations == 0) {
grpc_iomgr_shutdown();
census_shutdown();
grpc_timers_log_global_destroy();
}
gpr_mu_unlock(&g_init_mu);
}

@ -42,28 +42,20 @@
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
typedef struct {
void *unused;
} call_data;
typedef struct { void *unused; } call_data;
typedef struct {
grpc_mdelem *status;
grpc_mdelem *message;
} channel_data;
typedef struct { void *unused; } channel_data;
static void call_op(grpc_call_element *elem, grpc_call_element *from_elem,
grpc_call_op *op) {
channel_data *channeld = elem->channel_data;
GRPC_CALL_LOG_OP(GPR_INFO, elem, op);
switch (op->type) {
case GRPC_SEND_START:
grpc_call_recv_metadata(elem, grpc_mdelem_ref(channeld->status));
grpc_call_recv_metadata(elem, grpc_mdelem_ref(channeld->message));
grpc_call_stream_closed(elem);
break;
case GRPC_SEND_METADATA:
grpc_mdelem_unref(op->data.metadata);
grpc_metadata_batch_destroy(&op->data.metadata);
grpc_call_recv_synthetic_status(elem, GRPC_STATUS_UNKNOWN,
"Rpc sent on a lame channel.");
grpc_call_stream_closed(elem);
break;
default:
break;
@ -94,29 +86,17 @@ static void destroy_call_elem(grpc_call_element *elem) {}
static void init_channel_elem(grpc_channel_element *elem,
const grpc_channel_args *args, grpc_mdctx *mdctx,
int is_first, int is_last) {
channel_data *channeld = elem->channel_data;
char status[12];
GPR_ASSERT(is_first);
GPR_ASSERT(is_last);
channeld->message = grpc_mdelem_from_strings(mdctx, "grpc-message",
"Rpc sent on a lame channel.");
gpr_ltoa(GRPC_STATUS_UNKNOWN, status);
channeld->status = grpc_mdelem_from_strings(mdctx, "grpc-status", status);
}
static void destroy_channel_elem(grpc_channel_element *elem) {
channel_data *channeld = elem->channel_data;
grpc_mdelem_unref(channeld->message);
grpc_mdelem_unref(channeld->status);
}
static void destroy_channel_elem(grpc_channel_element *elem) {}
static const grpc_channel_filter lame_filter = {
call_op, channel_op, sizeof(call_data),
init_call_elem, destroy_call_elem, sizeof(channel_data),
init_channel_elem, destroy_channel_elem, "lame-client", };
call_op, channel_op, sizeof(call_data), init_call_elem, destroy_call_elem,
sizeof(channel_data), init_channel_elem, destroy_channel_elem,
"lame-client",
};
grpc_channel *grpc_lame_client_channel_create(void) {
static const grpc_channel_filter *filters[] = {&lame_filter};

@ -48,7 +48,7 @@
#include "src/core/iomgr/resolve_address.h"
#include "src/core/iomgr/tcp_client.h"
#include "src/core/security/auth.h"
#include "src/core/security/security_context.h"
#include "src/core/security/credentials.h"
#include "src/core/security/secure_transport_setup.h"
#include "src/core/support/string.h"
#include "src/core/surface/channel.h"
@ -74,7 +74,7 @@ typedef struct {
} request;
struct setup {
grpc_channel_security_context *security_context;
grpc_channel_security_connector *security_connector;
const char *target;
grpc_transport_setup_callback setup_callback;
void *setup_user_data;
@ -130,7 +130,7 @@ static void on_connect(void *rp, grpc_endpoint *tcp) {
return;
}
} else {
grpc_setup_secure_transport(&r->setup->security_context->base, tcp,
grpc_setup_secure_transport(&r->setup->security_connector->base, tcp,
on_secure_transport_setup_done, r);
}
}
@ -185,7 +185,7 @@ static void initiate_setup(void *sp, grpc_client_setup_request *cs_request) {
static void done_setup(void *sp) {
setup *s = sp;
gpr_free((void *)s->target);
grpc_security_context_unref(&s->security_context->base);
grpc_security_connector_unref(&s->security_connector->base);
gpr_free(s);
}
@ -203,24 +203,37 @@ static grpc_transport_setup_result complete_setup(void *channel_stack,
Asynchronously: - resolve target
- connect to it (trying alternatives as presented)
- perform handshakes */
grpc_channel *grpc_secure_channel_create_internal(
const char *target, const grpc_channel_args *args,
grpc_channel_security_context *context) {
grpc_channel *grpc_secure_channel_create(grpc_credentials *creds,
const char *target,
const grpc_channel_args *args) {
setup *s;
grpc_channel *channel;
grpc_arg context_arg;
grpc_arg connector_arg;
grpc_channel_args *args_copy;
grpc_mdctx *mdctx = grpc_mdctx_create();
grpc_channel_args *new_args_from_connector;
grpc_channel_security_connector* connector;
grpc_mdctx *mdctx;
#define MAX_FILTERS 3
const grpc_channel_filter *filters[MAX_FILTERS];
int n = 0;
if (grpc_find_security_context_in_args(args) != NULL) {
if (grpc_find_security_connector_in_args(args) != NULL) {
gpr_log(GPR_ERROR, "Cannot set security context in channel args.");
return grpc_lame_client_channel_create();
}
if (grpc_credentials_create_security_connector(
creds, target, args, NULL, &connector, &new_args_from_connector) !=
GRPC_SECURITY_OK) {
return grpc_lame_client_channel_create();
}
mdctx = grpc_credentials_get_or_create_metadata_context(creds);
s = gpr_malloc(sizeof(setup));
context_arg = grpc_security_context_to_arg(&context->base);
args_copy = grpc_channel_args_copy_and_add(args, &context_arg);
connector_arg = grpc_security_connector_to_arg(&connector->base);
args_copy = grpc_channel_args_copy_and_add(
new_args_from_connector != NULL ? new_args_from_connector : args,
&connector_arg);
filters[n++] = &grpc_client_surface_filter;
if (grpc_channel_args_is_census_enabled(args)) {
filters[n++] = &grpc_client_census_filter;
@ -229,13 +242,14 @@ grpc_channel *grpc_secure_channel_create_internal(
GPR_ASSERT(n <= MAX_FILTERS);
channel = grpc_channel_create_from_filters(filters, n, args_copy, mdctx, 1);
grpc_channel_args_destroy(args_copy);
if (new_args_from_connector != NULL) {
grpc_channel_args_destroy(new_args_from_connector);
}
s->target = gpr_strdup(target);
s->setup_callback = complete_setup;
s->setup_user_data = grpc_channel_get_channel_stack(channel);
s->security_context =
(grpc_channel_security_context *)grpc_security_context_ref(
&context->base);
s->security_connector = connector;
grpc_client_setup_create_and_attach(grpc_channel_get_channel_stack(channel),
args, mdctx, initiate_setup, done_setup,
s);

@ -411,29 +411,32 @@ static void read_closed(grpc_call_element *elem) {
gpr_mu_unlock(&chand->server->mu);
}
static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) {
grpc_call_element *elem = user_data;
channel_data *chand = elem->channel_data;
call_data *calld = elem->call_data;
if (md->key == chand->path_key) {
calld->path = grpc_mdstr_ref(md->value);
return NULL;
} else if (md->key == chand->authority_key) {
calld->host = grpc_mdstr_ref(md->value);
return NULL;
}
return md;
}
static void call_op(grpc_call_element *elem, grpc_call_element *from_elemn,
grpc_call_op *op) {
channel_data *chand = elem->channel_data;
call_data *calld = elem->call_data;
grpc_mdelem *md;
GRPC_CALL_LOG_OP(GPR_INFO, elem, op);
switch (op->type) {
case GRPC_RECV_METADATA:
md = op->data.metadata;
if (md->key == chand->path_key) {
calld->path = grpc_mdstr_ref(md->value);
grpc_mdelem_unref(md);
} else if (md->key == chand->authority_key) {
calld->host = grpc_mdstr_ref(md->value);
grpc_mdelem_unref(md);
} else {
grpc_call_recv_metadata(elem, md);
grpc_metadata_batch_filter(&op->data.metadata, server_filter, elem);
if (grpc_call_recv_metadata(elem, &op->data.metadata)) {
calld->deadline = op->data.metadata.deadline;
start_new_rpc(elem);
}
break;
case GRPC_RECV_END_OF_INITIAL_METADATA:
start_new_rpc(elem);
grpc_call_initial_metadata_complete(elem);
break;
case GRPC_RECV_MESSAGE:
grpc_call_recv_message(elem, op->data.message);
op->done_cb(op->user_data, GRPC_OP_OK);
@ -444,10 +447,6 @@ static void call_op(grpc_call_element *elem, grpc_call_element *from_elemn,
case GRPC_RECV_FINISH:
stream_closed(elem);
break;
case GRPC_RECV_DEADLINE:
grpc_call_set_deadline(elem, op->data.deadline);
((call_data *)elem->call_data)->deadline = op->data.deadline;
break;
default:
GPR_ASSERT(op->dir == GRPC_CALL_DOWN);
grpc_call_next_op(elem, op);
@ -464,7 +463,8 @@ static void channel_op(grpc_channel_element *elem,
case GRPC_ACCEPT_CALL:
/* create a call */
grpc_call_create(chand->channel, NULL,
op->data.accept_call.transport_server_data);
op->data.accept_call.transport_server_data, NULL, 0,
gpr_inf_future);
break;
case GRPC_TRANSPORT_CLOSED:
/* if the transport is closed for a server channel, we destroy the

@ -54,6 +54,7 @@ typedef struct {
gpr_uint8 process_ping_reply;
gpr_uint8 goaway;
gpr_int64 initial_window_update;
gpr_uint32 window_update;
gpr_uint32 goaway_last_stream_index;
gpr_uint32 goaway_error;

@ -218,6 +218,14 @@ grpc_chttp2_parse_error grpc_chttp2_settings_parser_parse(
return GRPC_CHTTP2_CONNECTION_ERROR;
}
}
if (parser->id == GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE &&
parser->incoming_settings[parser->id] != parser->value) {
state->initial_window_update =
(gpr_int64)parser->value -
parser->incoming_settings[parser->id];
gpr_log(GPR_DEBUG, "adding %d for initial_window change",
(int)state->initial_window_update);
}
parser->incoming_settings[parser->id] = parser->value;
if (grpc_http_trace) {
gpr_log(GPR_DEBUG, "CHTTP2: got setting %d = %d", parser->id,

@ -43,7 +43,7 @@
#include "src/core/transport/chttp2/timeout_encoding.h"
#include "src/core/transport/chttp2/varint.h"
#define HASH_FRAGMENT_1(x) ((x) & 255)
#define HASH_FRAGMENT_1(x) ((x)&255)
#define HASH_FRAGMENT_2(x) ((x >> 8) & 255)
#define HASH_FRAGMENT_3(x) ((x >> 16) & 255)
#define HASH_FRAGMENT_4(x) ((x >> 24) & 255)
@ -171,13 +171,15 @@ static gpr_uint8 *add_tiny_header_data(framer_state *st, int len) {
return gpr_slice_buffer_tiny_add(st->output, len);
}
static void add_elem(grpc_chttp2_hpack_compressor *c, grpc_mdelem *elem) {
/* add an element to the decoder table: returns metadata element to unref */
static grpc_mdelem *add_elem(grpc_chttp2_hpack_compressor *c,
grpc_mdelem *elem) {
gpr_uint32 key_hash = elem->key->hash;
gpr_uint32 elem_hash = GRPC_MDSTR_KV_HASH(key_hash, elem->value->hash);
gpr_uint32 new_index = c->tail_remote_index + c->table_elems + 1;
gpr_uint32 elem_size = 32 + GPR_SLICE_LENGTH(elem->key->slice) +
GPR_SLICE_LENGTH(elem->value->slice);
int drop_ref;
grpc_mdelem *elem_to_unref;
/* Reserve space for this element in the remote table: if this overflows
the current table, drop elements until it fits, matching the decompressor
@ -204,34 +206,32 @@ static void add_elem(grpc_chttp2_hpack_compressor *c, grpc_mdelem *elem) {
if (c->entries_elems[HASH_FRAGMENT_2(elem_hash)] == elem) {
/* already there: update with new index */
c->indices_elems[HASH_FRAGMENT_2(elem_hash)] = new_index;
drop_ref = 1;
elem_to_unref = elem;
} else if (c->entries_elems[HASH_FRAGMENT_3(elem_hash)] == elem) {
/* already there (cuckoo): update with new index */
c->indices_elems[HASH_FRAGMENT_3(elem_hash)] = new_index;
drop_ref = 1;
elem_to_unref = elem;
} else if (c->entries_elems[HASH_FRAGMENT_2(elem_hash)] == NULL) {
/* not there, but a free element: add */
c->entries_elems[HASH_FRAGMENT_2(elem_hash)] = elem;
c->indices_elems[HASH_FRAGMENT_2(elem_hash)] = new_index;
drop_ref = 0;
elem_to_unref = NULL;
} else if (c->entries_elems[HASH_FRAGMENT_3(elem_hash)] == NULL) {
/* not there (cuckoo), but a free element: add */
c->entries_elems[HASH_FRAGMENT_3(elem_hash)] = elem;
c->indices_elems[HASH_FRAGMENT_3(elem_hash)] = new_index;
drop_ref = 0;
elem_to_unref = NULL;
} else if (c->indices_elems[HASH_FRAGMENT_2(elem_hash)] <
c->indices_elems[HASH_FRAGMENT_3(elem_hash)]) {
/* not there: replace oldest */
grpc_mdelem_unref(c->entries_elems[HASH_FRAGMENT_2(elem_hash)]);
elem_to_unref = c->entries_elems[HASH_FRAGMENT_2(elem_hash)];
c->entries_elems[HASH_FRAGMENT_2(elem_hash)] = elem;
c->indices_elems[HASH_FRAGMENT_2(elem_hash)] = new_index;
drop_ref = 0;
} else {
/* not there: replace oldest */
grpc_mdelem_unref(c->entries_elems[HASH_FRAGMENT_3(elem_hash)]);
elem_to_unref = c->entries_elems[HASH_FRAGMENT_3(elem_hash)];
c->entries_elems[HASH_FRAGMENT_3(elem_hash)] = elem;
c->indices_elems[HASH_FRAGMENT_3(elem_hash)] = new_index;
drop_ref = 0;
}
/* do exactly the same for the key (so we can find by that again too) */
@ -257,9 +257,7 @@ static void add_elem(grpc_chttp2_hpack_compressor *c, grpc_mdelem *elem) {
c->indices_keys[HASH_FRAGMENT_3(key_hash)] = new_index;
}
if (drop_ref) {
grpc_mdelem_unref(elem);
}
return elem_to_unref;
}
static void emit_indexed(grpc_chttp2_hpack_compressor *c, gpr_uint32 index,
@ -348,9 +346,9 @@ static gpr_uint32 dynidx(grpc_chttp2_hpack_compressor *c, gpr_uint32 index) {
c->table_elems - index;
}
/* encode an mdelem, taking ownership of it */
static void hpack_enc(grpc_chttp2_hpack_compressor *c, grpc_mdelem *elem,
framer_state *st) {
/* encode an mdelem; returns metadata element to unref */
static grpc_mdelem *hpack_enc(grpc_chttp2_hpack_compressor *c,
grpc_mdelem *elem, framer_state *st) {
gpr_uint32 key_hash = elem->key->hash;
gpr_uint32 elem_hash = GRPC_MDSTR_KV_HASH(key_hash, elem->value->hash);
size_t decoder_space_usage;
@ -366,8 +364,7 @@ static void hpack_enc(grpc_chttp2_hpack_compressor *c, grpc_mdelem *elem,
/* HIT: complete element (first cuckoo hash) */
emit_indexed(c, dynidx(c, c->indices_elems[HASH_FRAGMENT_2(elem_hash)]),
st);
grpc_mdelem_unref(elem);
return;
return elem;
}
if (c->entries_elems[HASH_FRAGMENT_3(elem_hash)] == elem &&
@ -375,8 +372,7 @@ static void hpack_enc(grpc_chttp2_hpack_compressor *c, grpc_mdelem *elem,
/* HIT: complete element (second cuckoo hash) */
emit_indexed(c, dynidx(c, c->indices_elems[HASH_FRAGMENT_3(elem_hash)]),
st);
grpc_mdelem_unref(elem);
return;
return elem;
}
/* should this elem be in the table? */
@ -394,12 +390,12 @@ static void hpack_enc(grpc_chttp2_hpack_compressor *c, grpc_mdelem *elem,
/* HIT: key (first cuckoo hash) */
if (should_add_elem) {
emit_lithdr_incidx(c, dynidx(c, indices_key), elem, st);
add_elem(c, elem);
return add_elem(c, elem);
} else {
emit_lithdr_noidx(c, dynidx(c, indices_key), elem, st);
grpc_mdelem_unref(elem);
return elem;
}
return;
abort();
}
indices_key = c->indices_keys[HASH_FRAGMENT_3(key_hash)];
@ -408,23 +404,24 @@ static void hpack_enc(grpc_chttp2_hpack_compressor *c, grpc_mdelem *elem,
/* HIT: key (first cuckoo hash) */
if (should_add_elem) {
emit_lithdr_incidx(c, dynidx(c, indices_key), elem, st);
add_elem(c, elem);
return add_elem(c, elem);
} else {
emit_lithdr_noidx(c, dynidx(c, indices_key), elem, st);
grpc_mdelem_unref(elem);
return elem;
}
return;
abort();
}
/* no elem, key in the table... fall back to literal emission */
if (should_add_elem) {
emit_lithdr_incidx_v(c, elem, st);
add_elem(c, elem);
return add_elem(c, elem);
} else {
emit_lithdr_noidx_v(c, elem, st);
grpc_mdelem_unref(elem);
return elem;
}
abort();
}
#define STRLEN_LIT(x) (sizeof(x) - 1)
@ -433,11 +430,13 @@ static void hpack_enc(grpc_chttp2_hpack_compressor *c, grpc_mdelem *elem,
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);
hpack_enc(c, grpc_mdelem_from_metadata_strings(
c->mdctx, grpc_mdstr_ref(c->timeout_key_str),
grpc_mdstr_from_string(c->mdctx, timeout_str)),
st);
mdelem = grpc_mdelem_from_metadata_strings(
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);
}
gpr_slice grpc_chttp2_data_frame_create_empty_close(gpr_uint32 id) {
@ -480,10 +479,9 @@ gpr_uint32 grpc_chttp2_preencode(grpc_stream_op *inops, size_t *inops_count,
/* skip */
curop++;
break;
case GRPC_OP_FLOW_CTL_CB:
case GRPC_OP_DEADLINE:
case GRPC_OP_METADATA:
case GRPC_OP_METADATA_BOUNDARY:
grpc_metadata_batch_assert_ok(&op->data.metadata);
case GRPC_OP_FLOW_CTL_CB:
/* these just get copied as they don't impact the number of flow
controlled bytes */
grpc_sopb_append(outops, op, 1);
@ -530,6 +528,12 @@ exit_loop:
*inops_count -= curop;
memmove(inops, inops + curop, *inops_count * sizeof(grpc_stream_op));
for (curop = 0; curop < *inops_count; curop++) {
if (inops[curop].type == GRPC_OP_METADATA) {
grpc_metadata_batch_assert_ok(&inops[curop].data.metadata);
}
}
return flow_controlled_bytes_taken;
}
@ -542,6 +546,10 @@ void grpc_chttp2_encode(grpc_stream_op *ops, size_t ops_count, int eof,
grpc_stream_op *op;
gpr_uint32 max_take_size;
gpr_uint32 curop = 0;
gpr_uint32 unref_op;
grpc_mdctx *mdctx = compressor->mdctx;
grpc_linked_mdelem *l;
int need_unref = 0;
GPR_ASSERT(stream_id != 0);
@ -564,14 +572,19 @@ void grpc_chttp2_encode(grpc_stream_op *ops, size_t ops_count, int eof,
curop++;
break;
case GRPC_OP_METADATA:
hpack_enc(compressor, op->data.metadata, &st);
curop++;
break;
case GRPC_OP_DEADLINE:
deadline_enc(compressor, op->data.deadline, &st);
curop++;
break;
case GRPC_OP_METADATA_BOUNDARY:
/* Encode a metadata batch; store the returned values, representing
a metadata element that needs to be unreffed back into the metadata
slot. THIS MAY NOT BE THE SAME ELEMENT (if a decoder table slot got
updated). After this loop, we'll do a batch unref of elements. */
need_unref |= op->data.metadata.garbage.head != NULL;
grpc_metadata_batch_assert_ok(&op->data.metadata);
for (l = op->data.metadata.list.head; l; l = l->next) {
l->md = hpack_enc(compressor, l->md, &st);
need_unref |= l->md != NULL;
}
if (gpr_time_cmp(op->data.metadata.deadline, gpr_inf_future) != 0) {
deadline_enc(compressor, op->data.metadata.deadline, &st);
}
ensure_frame_type(&st, HEADER, 0);
finish_frame(&st, 1, 0);
st.last_was_header = 0; /* force a new header frame */
@ -601,4 +614,19 @@ void grpc_chttp2_encode(grpc_stream_op *ops, size_t ops_count, int eof,
begin_frame(&st, DATA);
}
finish_frame(&st, 1, eof);
if (need_unref) {
grpc_mdctx_lock(mdctx);
for (unref_op = 0; unref_op < curop; unref_op++) {
op = &ops[unref_op];
if (op->type != GRPC_OP_METADATA) continue;
for (l = op->data.metadata.list.head; l; l = l->next) {
if (l->md) grpc_mdctx_locked_mdelem_unref(mdctx, l->md);
}
for (l = op->data.metadata.garbage.head; l; l = l->next) {
grpc_mdctx_locked_mdelem_unref(mdctx, l->md);
}
}
grpc_mdctx_unlock(mdctx);
}
}

@ -68,10 +68,10 @@ int grpc_http_trace = 0;
typedef struct transport transport;
typedef struct stream stream;
#define IF_TRACING(stmt) \
if (!(grpc_http_trace)) \
; \
else \
#define IF_TRACING(stmt) \
if (!(grpc_http_trace)) \
; \
else \
stmt
/* streams are kept in various linked lists depending on what things need to
@ -276,8 +276,8 @@ struct transport {
struct stream {
gpr_uint32 id;
gpr_uint32 outgoing_window;
gpr_uint32 incoming_window;
gpr_int64 outgoing_window;
/* when the application requests writes be closed, the write_closed is
'queued'; when the close is flow controlled into the send path, we are
'sending' it; when the write has been performed it is 'sent' */
@ -292,6 +292,12 @@ struct stream {
stream_link links[STREAM_LIST_COUNT];
gpr_uint8 included[STREAM_LIST_COUNT];
/* incoming metadata */
grpc_linked_mdelem *incoming_metadata;
size_t incoming_metadata_count;
size_t incoming_metadata_capacity;
gpr_timespec incoming_deadline;
/* sops from application */
grpc_stream_op_buffer outgoing_sopb;
/* sops that have passed flow control to be written */
@ -577,7 +583,7 @@ static int init_stream(grpc_transport *gt, grpc_stream *gs,
lock(t);
s->id = 0;
} else {
s->id = (gpr_uint32)(gpr_uintptr) server_data;
s->id = (gpr_uint32)(gpr_uintptr)server_data;
t->incoming_stream = s;
grpc_chttp2_stream_map_add(&t->stream_map, s->id, s);
}
@ -593,6 +599,10 @@ static int init_stream(grpc_transport *gt, grpc_stream *gs,
s->cancelled = 0;
s->allow_window_updates = 0;
s->published_close = 0;
s->incoming_metadata_count = 0;
s->incoming_metadata_capacity = 0;
s->incoming_metadata = NULL;
s->incoming_deadline = gpr_inf_future;
memset(&s->links, 0, sizeof(s->links));
memset(&s->included, 0, sizeof(s->included));
grpc_sopb_init(&s->outgoing_sopb);
@ -698,7 +708,8 @@ static void stream_list_add_tail(transport *t, stream *s, stream_list_id id) {
}
static void stream_list_join(transport *t, stream *s, stream_list_id id) {
if (id == PENDING_CALLBACKS) GPR_ASSERT(t->cb != NULL || t->error_state == ERROR_STATE_NONE);
if (id == PENDING_CALLBACKS)
GPR_ASSERT(t->cb != NULL || t->error_state == ERROR_STATE_NONE);
if (s->included[id]) {
return;
}
@ -760,7 +771,7 @@ static void unlock(transport *t) {
if (t->error_state == ERROR_STATE_SEEN && !t->writing) {
call_closed = 1;
t->calling_back = 1;
t->cb = NULL; /* no more callbacks */
t->cb = NULL; /* no more callbacks */
t->error_state = ERROR_STATE_NOTIFIED;
}
if (t->num_pending_goaways) {
@ -782,8 +793,7 @@ static void unlock(transport *t) {
/* perform some callbacks if necessary */
for (i = 0; i < num_goaways; i++) {
cb->goaway(t->cb_user_data, &t->base, goaways[i].status,
goaways[i].debug);
cb->goaway(t->cb_user_data, &t->base, goaways[i].status, goaways[i].debug);
}
if (perform_callbacks) {
@ -834,13 +844,10 @@ static void push_setting(transport *t, grpc_chttp2_setting_id id,
static int prepare_write(transport *t) {
stream *s;
gpr_slice_buffer tempbuf;
gpr_uint32 window_delta;
/* simple writes are queued to qbuf, and flushed here */
tempbuf = t->qbuf;
t->qbuf = t->outbuf;
t->outbuf = tempbuf;
gpr_slice_buffer_swap(&t->qbuf, &t->outbuf);
GPR_ASSERT(t->qbuf.count == 0);
if (t->dirtied_local_settings && !t->sent_local_settings) {
@ -855,7 +862,8 @@ static int prepare_write(transport *t) {
/* for each stream that's become writable, frame it's data (according to
available window sizes) and add to the output buffer */
while (t->outgoing_window && (s = stream_list_remove_head(t, WRITABLE))) {
while (t->outgoing_window && (s = stream_list_remove_head(t, WRITABLE)) &&
s->outgoing_window > 0) {
window_delta = grpc_chttp2_preencode(
s->outgoing_sopb.ops, &s->outgoing_sopb.nops,
GPR_MIN(t->outgoing_window, s->outgoing_window), &s->writing_sopb);
@ -870,7 +878,7 @@ static int prepare_write(transport *t) {
/* if there are still writes to do and the stream still has window
available, then schedule a further write */
if (s->outgoing_sopb.nops && s->outgoing_window) {
if (s->outgoing_sopb.nops > 0 && s->outgoing_window > 0) {
GPR_ASSERT(!t->outgoing_window);
stream_list_add_tail(t, s, WRITABLE);
}
@ -1060,6 +1068,17 @@ static void finalize_cancellations(transport *t) {
}
}
static void add_incoming_metadata(transport *t, stream *s, grpc_mdelem *elem) {
if (s->incoming_metadata_capacity == s->incoming_metadata_count) {
s->incoming_metadata_capacity =
GPR_MAX(8, 2 * s->incoming_metadata_capacity);
s->incoming_metadata =
gpr_realloc(s->incoming_metadata, sizeof(*s->incoming_metadata) *
s->incoming_metadata_capacity);
}
s->incoming_metadata[s->incoming_metadata_count++].md = elem;
}
static void cancel_stream_inner(transport *t, stream *s, gpr_uint32 id,
grpc_status_code local_status,
grpc_chttp2_error_code error_code,
@ -1079,9 +1098,18 @@ static void cancel_stream_inner(transport *t, stream *s, gpr_uint32 id,
stream_list_join(t, s, CANCELLED);
gpr_ltoa(local_status, buffer);
grpc_sopb_add_metadata(
&s->parser.incoming_sopb,
add_incoming_metadata(
t, s,
grpc_mdelem_from_strings(t->metadata_context, "grpc-status", buffer));
switch (local_status) {
case GRPC_STATUS_CANCELLED:
add_incoming_metadata(
t, s, grpc_mdelem_from_strings(t->metadata_context,
"grpc-message", "Cancelled"));
break;
default:
break;
}
stream_list_join(t, s, PENDING_CALLBACKS);
}
@ -1257,11 +1285,10 @@ static void on_header(void *tp, grpc_mdelem *md) {
}
grpc_mdelem_set_user_data(md, free_timeout, cached_timeout);
}
grpc_sopb_add_deadline(&s->parser.incoming_sopb,
gpr_time_add(gpr_now(), *cached_timeout));
s->incoming_deadline = gpr_time_add(gpr_now(), *cached_timeout);
grpc_mdelem_unref(md);
} else {
grpc_sopb_add_metadata(&s->parser.incoming_sopb, md);
add_incoming_metadata(t, s, md);
}
}
@ -1306,7 +1333,7 @@ static int init_header_frame_parser(transport *t, int is_continuation) {
t->incoming_stream = NULL;
/* if stream is accepted, we set incoming_stream in init_stream */
t->cb->accept_stream(t->cb_user_data, &t->base,
(void *)(gpr_uintptr) t->incoming_stream_id);
(void *)(gpr_uintptr)t->incoming_stream_id);
s = t->incoming_stream;
if (!s) {
gpr_log(GPR_ERROR, "stream not accepted");
@ -1433,8 +1460,37 @@ static int init_frame_parser(transport *t) {
}
}
static int is_window_update_legal(gpr_uint32 window_update, gpr_uint32 window) {
return window_update < MAX_WINDOW - window;
static int is_window_update_legal(gpr_int64 window_update, gpr_int64 window) {
return window + window_update < MAX_WINDOW;
}
static void free_md(void *p, grpc_op_error result) { gpr_free(p); }
static void add_metadata_batch(transport *t, stream *s) {
grpc_metadata_batch b;
size_t i;
b.list.head = &s->incoming_metadata[0];
b.list.tail = &s->incoming_metadata[s->incoming_metadata_count - 1];
b.garbage.head = b.garbage.tail = NULL;
b.deadline = s->incoming_deadline;
for (i = 1; i < s->incoming_metadata_count; i++) {
s->incoming_metadata[i].prev = &s->incoming_metadata[i - 1];
s->incoming_metadata[i - 1].next = &s->incoming_metadata[i];
}
s->incoming_metadata[0].prev = NULL;
s->incoming_metadata[s->incoming_metadata_count - 1].next = NULL;
grpc_sopb_add_metadata(&s->parser.incoming_sopb, b);
grpc_sopb_add_flow_ctl_cb(&s->parser.incoming_sopb, free_md,
s->incoming_metadata);
/* reset */
s->incoming_deadline = gpr_inf_future;
s->incoming_metadata = NULL;
s->incoming_metadata_count = 0;
s->incoming_metadata_capacity = 0;
}
static int parse_frame_slice(transport *t, gpr_slice slice, int is_last) {
@ -1451,8 +1507,7 @@ static int parse_frame_slice(transport *t, gpr_slice slice, int is_last) {
stream_list_join(t, t->incoming_stream, PENDING_CALLBACKS);
}
if (st.metadata_boundary) {
grpc_sopb_add_metadata_boundary(
&t->incoming_stream->parser.incoming_sopb);
add_metadata_batch(t, t->incoming_stream);
stream_list_join(t, t->incoming_stream, PENDING_CALLBACKS);
}
if (st.ack_settings) {
@ -1488,12 +1543,23 @@ static int parse_frame_slice(transport *t, gpr_slice slice, int is_last) {
}
}
}
if (st.initial_window_update) {
for (i = 0; i < t->stream_map.count; i++) {
stream *s = (stream*)(t->stream_map.values[i]);
int was_window_empty = s->outgoing_window <= 0;
s->outgoing_window += st.initial_window_update;
if (was_window_empty && s->outgoing_window > 0 &&
s->outgoing_sopb.nops > 0) {
stream_list_join(t, s, WRITABLE);
}
}
}
if (st.window_update) {
if (t->incoming_stream_id) {
/* if there was a stream id, this is for some stream */
stream *s = lookup_stream(t, t->incoming_stream_id);
if (s) {
int was_window_empty = s->outgoing_window == 0;
int was_window_empty = s->outgoing_window <= 0;
if (!is_window_update_legal(st.window_update, s->outgoing_window)) {
cancel_stream(t, s, grpc_chttp2_http2_error_to_grpc_status(
GRPC_CHTTP2_FLOW_CONTROL_ERROR),
@ -1571,8 +1637,8 @@ static int process_read(transport *t, gpr_slice slice) {
"Connect string mismatch: expected '%c' (%d) got '%c' (%d) "
"at byte %d",
CLIENT_CONNECT_STRING[t->deframe_state],
(int)(gpr_uint8) CLIENT_CONNECT_STRING[t->deframe_state],
*cur, (int)*cur, t->deframe_state);
(int)(gpr_uint8)CLIENT_CONNECT_STRING[t->deframe_state], *cur,
(int)*cur, t->deframe_state);
drop_connection(t);
return 0;
}
@ -1769,17 +1835,20 @@ static int prepare_callbacks(transport *t) {
int n = 0;
while ((s = stream_list_remove_head(t, PENDING_CALLBACKS))) {
int execute = 1;
grpc_sopb_swap(&s->parser.incoming_sopb, &s->callback_sopb);
s->callback_state = compute_state(s->sent_write_closed, s->read_closed);
if (s->callback_state == GRPC_STREAM_CLOSED) {
remove_from_stream_map(t, s);
if (s->published_close) {
execute = 0;
} else if (s->incoming_metadata_count) {
add_metadata_batch(t, s);
}
s->published_close = 1;
}
grpc_sopb_swap(&s->parser.incoming_sopb, &s->callback_sopb);
if (execute) {
stream_list_add_tail(t, s, EXECUTING_CALLBACKS);
n = 1;
@ -1816,9 +1885,9 @@ static void add_to_pollset(grpc_transport *gt, grpc_pollset *pollset) {
*/
static const grpc_transport_vtable vtable = {
sizeof(stream), init_stream, send_batch, set_allow_window_updates,
add_to_pollset, destroy_stream, abort_stream, goaway,
close_transport, send_ping, destroy_transport};
sizeof(stream), init_stream, send_batch, set_allow_window_updates,
add_to_pollset, destroy_stream, abort_stream, goaway, close_transport,
send_ping, destroy_transport};
void grpc_create_chttp2_transport(grpc_transport_setup_callback setup,
void *arg,

@ -34,10 +34,12 @@
#include "src/core/iomgr/sockaddr.h"
#include "src/core/transport/metadata.h"
#include <assert.h>
#include <stddef.h>
#include <string.h>
#include <grpc/support/alloc.h>
#include <grpc/support/atm.h>
#include <grpc/support/log.h>
#include "src/core/support/murmur_hash.h"
#include "src/core/transport/chttp2/bin_encoder.h"
@ -68,11 +70,12 @@ typedef struct internal_metadata {
internal_string *key;
internal_string *value;
gpr_atm refcnt;
/* private only data */
void *user_data;
void (*destroy_user_data)(void *user_data);
gpr_uint32 refs;
grpc_mdctx *context;
struct internal_metadata *bucket_next;
} internal_metadata;
@ -117,7 +120,7 @@ static void unlock(grpc_mdctx *ctx) {
if (ctx->refs == 0) {
/* uncomment if you're having trouble diagnosing an mdelem leak to make
things clearer (slows down destruction a lot, however) */
/* gc_mdtab(ctx); */
gc_mdtab(ctx);
if (ctx->mdtab_count && ctx->mdtab_count == ctx->mdtab_free) {
discard_metadata(ctx);
}
@ -129,8 +132,8 @@ static void unlock(grpc_mdctx *ctx) {
gpr_mu_unlock(&ctx->mu);
}
static void ref_md(internal_metadata *md) {
if (0 == md->refs++) {
static void ref_md_locked(internal_metadata *md) {
if (0 == gpr_atm_no_barrier_fetch_add(&md->refcnt, 1)) {
md->context->mdtab_free--;
}
}
@ -168,7 +171,7 @@ static void discard_metadata(grpc_mdctx *ctx) {
for (i = 0; i < ctx->mdtab_capacity; i++) {
cur = ctx->mdtab[i];
while (cur) {
GPR_ASSERT(cur->refs == 0);
GPR_ASSERT(gpr_atm_acq_load(&cur->refcnt) == 0);
next = cur->bucket_next;
internal_string_unref(cur->key);
internal_string_unref(cur->value);
@ -349,7 +352,7 @@ static void gc_mdtab(grpc_mdctx *ctx) {
prev_next = &ctx->mdtab[i];
for (md = ctx->mdtab[i]; md; md = next) {
next = md->bucket_next;
if (md->refs == 0) {
if (gpr_atm_acq_load(&md->refcnt) == 0) {
internal_string_unref(md->key);
internal_string_unref(md->value);
if (md->user_data) {
@ -415,7 +418,7 @@ 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(md);
ref_md_locked(md);
internal_string_unref(key);
internal_string_unref(value);
unlock(ctx);
@ -425,7 +428,7 @@ grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_mdctx *ctx,
/* not found: create a new pair */
md = gpr_malloc(sizeof(internal_metadata));
md->refs = 1;
gpr_atm_rel_store(&md->refcnt, 1);
md->context = ctx;
md->key = key;
md->value = value;
@ -468,10 +471,12 @@ grpc_mdelem *grpc_mdelem_from_string_and_buffer(grpc_mdctx *ctx,
grpc_mdelem *grpc_mdelem_ref(grpc_mdelem *gmd) {
internal_metadata *md = (internal_metadata *)gmd;
grpc_mdctx *ctx = md->context;
lock(ctx);
ref_md(md);
unlock(ctx);
/* we can assume the ref count is >= 1 as the application is calling
this function - meaning that no adjustment to mdtab_free is necessary,
simplifying the logic here to be just an atomic increment */
/* use C assert to have this removed in opt builds */
assert(gpr_atm_no_barrier_load(&md->refcnt) >= 1);
gpr_atm_no_barrier_fetch_add(&md->refcnt, 1);
return gmd;
}
@ -479,8 +484,8 @@ void grpc_mdelem_unref(grpc_mdelem *gmd) {
internal_metadata *md = (internal_metadata *)gmd;
grpc_mdctx *ctx = md->context;
lock(ctx);
GPR_ASSERT(md->refs);
if (0 == --md->refs) {
assert(gpr_atm_no_barrier_load(&md->refcnt) >= 1);
if (1 == gpr_atm_full_fetch_add(&md->refcnt, -1)) {
ctx->mdtab_free++;
}
unlock(ctx);
@ -550,3 +555,17 @@ gpr_slice grpc_mdstr_as_base64_encoded_and_huffman_compressed(grpc_mdstr *gs) {
unlock(ctx);
return slice;
}
void grpc_mdctx_lock(grpc_mdctx *ctx) { lock(ctx); }
void grpc_mdctx_locked_mdelem_unref(grpc_mdctx *ctx, grpc_mdelem *gmd) {
internal_metadata *md = (internal_metadata *)gmd;
grpc_mdctx *elem_ctx = md->context;
GPR_ASSERT(ctx == elem_ctx);
assert(gpr_atm_no_barrier_load(&md->refcnt) >= 1);
if (1 == gpr_atm_full_fetch_add(&md->refcnt, -1)) {
ctx->mdtab_free++;
}
}
void grpc_mdctx_unlock(grpc_mdctx *ctx) { unlock(ctx); }

@ -135,6 +135,18 @@ void grpc_mdelem_unref(grpc_mdelem *md);
Does not promise that the returned string has no embedded nulls however. */
const char *grpc_mdstr_as_c_string(grpc_mdstr *s);
/* Batch mode metadata functions.
These API's have equivalents above, but allow taking the mdctx just once,
performing a bunch of work, and then leaving the mdctx. */
/* Lock the metadata context: it's only safe to call _locked_ functions against
this context from the calling thread until grpc_mdctx_unlock is called */
void grpc_mdctx_lock(grpc_mdctx *ctx);
/* Unref a metadata element */
void grpc_mdctx_locked_mdelem_unref(grpc_mdctx *ctx, grpc_mdelem *elem);
/* Unlock the metadata context */
void grpc_mdctx_unlock(grpc_mdctx *ctx);
#define GRPC_MDSTR_KV_HASH(k_hash, v_hash) (GPR_ROTL((k_hash), 2) ^ (v_hash))
#endif /* GRPC_INTERNAL_CORE_TRANSPORT_METADATA_H */

@ -33,11 +33,11 @@
#include "src/core/transport/stream_op.h"
#include <string.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <string.h>
/* Exponential growth function: Given x, return a larger x.
Currently we grow by 1.5 times upon reallocation. */
#define GROW(x) (3 * (x) / 2)
@ -79,33 +79,46 @@ void grpc_stream_ops_unref_owned_objects(grpc_stream_op *ops, size_t nops) {
gpr_slice_unref(ops[i].data.slice);
break;
case GRPC_OP_METADATA:
grpc_mdelem_unref(ops[i].data.metadata);
grpc_metadata_batch_destroy(&ops[i].data.metadata);
break;
case GRPC_OP_FLOW_CTL_CB:
ops[i].data.flow_ctl_cb.cb(ops[i].data.flow_ctl_cb.arg, GRPC_OP_ERROR);
break;
case GRPC_NO_OP:
case GRPC_OP_DEADLINE:
case GRPC_OP_METADATA_BOUNDARY:
case GRPC_OP_BEGIN_MESSAGE:
break;
}
}
}
static void assert_contained_metadata_ok(grpc_stream_op *ops, size_t nops) {
#ifndef NDEBUG
size_t i;
for (i = 0; i < nops; i++) {
if (ops[i].type == GRPC_OP_METADATA) {
grpc_metadata_batch_assert_ok(&ops[i].data.metadata);
}
}
#endif /* NDEBUG */
}
static void expandto(grpc_stream_op_buffer *sopb, size_t new_capacity) {
sopb->capacity = new_capacity;
assert_contained_metadata_ok(sopb->ops, sopb->nops);
if (sopb->ops == sopb->inlined_ops) {
sopb->ops = gpr_malloc(sizeof(grpc_stream_op) * new_capacity);
memcpy(sopb->ops, sopb->inlined_ops, sopb->nops * sizeof(grpc_stream_op));
} else {
sopb->ops = gpr_realloc(sopb->ops, sizeof(grpc_stream_op) * new_capacity);
}
assert_contained_metadata_ok(sopb->ops, sopb->nops);
}
static grpc_stream_op *add(grpc_stream_op_buffer *sopb) {
grpc_stream_op *out;
assert_contained_metadata_ok(sopb->ops, sopb->nops);
if (sopb->nops == sopb->capacity) {
expandto(sopb, GROW(sopb->capacity));
}
@ -116,6 +129,7 @@ static grpc_stream_op *add(grpc_stream_op_buffer *sopb) {
void grpc_sopb_add_no_op(grpc_stream_op_buffer *sopb) {
add(sopb)->type = GRPC_NO_OP;
assert_contained_metadata_ok(sopb->ops, sopb->nops);
}
void grpc_sopb_add_begin_message(grpc_stream_op_buffer *sopb, gpr_uint32 length,
@ -124,30 +138,24 @@ void grpc_sopb_add_begin_message(grpc_stream_op_buffer *sopb, gpr_uint32 length,
op->type = GRPC_OP_BEGIN_MESSAGE;
op->data.begin_message.length = length;
op->data.begin_message.flags = flags;
assert_contained_metadata_ok(sopb->ops, sopb->nops);
}
void grpc_sopb_add_metadata_boundary(grpc_stream_op_buffer *sopb) {
grpc_stream_op *op = add(sopb);
op->type = GRPC_OP_METADATA_BOUNDARY;
}
void grpc_sopb_add_metadata(grpc_stream_op_buffer *sopb, grpc_mdelem *md) {
void grpc_sopb_add_metadata(grpc_stream_op_buffer *sopb,
grpc_metadata_batch b) {
grpc_stream_op *op = add(sopb);
grpc_metadata_batch_assert_ok(&b);
op->type = GRPC_OP_METADATA;
op->data.metadata = md;
}
void grpc_sopb_add_deadline(grpc_stream_op_buffer *sopb,
gpr_timespec deadline) {
grpc_stream_op *op = add(sopb);
op->type = GRPC_OP_DEADLINE;
op->data.deadline = deadline;
op->data.metadata = b;
grpc_metadata_batch_assert_ok(&op->data.metadata);
assert_contained_metadata_ok(sopb->ops, sopb->nops);
}
void grpc_sopb_add_slice(grpc_stream_op_buffer *sopb, gpr_slice slice) {
grpc_stream_op *op = add(sopb);
op->type = GRPC_OP_SLICE;
op->data.slice = slice;
assert_contained_metadata_ok(sopb->ops, sopb->nops);
}
void grpc_sopb_add_flow_ctl_cb(grpc_stream_op_buffer *sopb,
@ -157,6 +165,7 @@ void grpc_sopb_add_flow_ctl_cb(grpc_stream_op_buffer *sopb,
op->type = GRPC_OP_FLOW_CTL_CB;
op->data.flow_ctl_cb.cb = cb;
op->data.flow_ctl_cb.arg = arg;
assert_contained_metadata_ok(sopb->ops, sopb->nops);
}
void grpc_sopb_append(grpc_stream_op_buffer *sopb, grpc_stream_op *ops,
@ -164,10 +173,161 @@ void grpc_sopb_append(grpc_stream_op_buffer *sopb, grpc_stream_op *ops,
size_t orig_nops = sopb->nops;
size_t new_nops = orig_nops + nops;
assert_contained_metadata_ok(ops, nops);
assert_contained_metadata_ok(sopb->ops, sopb->nops);
if (new_nops > sopb->capacity) {
expandto(sopb, GPR_MAX(GROW(sopb->capacity), new_nops));
}
memcpy(sopb->ops + orig_nops, ops, sizeof(grpc_stream_op) * nops);
sopb->nops = new_nops;
assert_contained_metadata_ok(sopb->ops, sopb->nops);
}
static void assert_valid_list(grpc_mdelem_list *list) {
#ifndef NDEBUG
grpc_linked_mdelem *l;
GPR_ASSERT((list->head == NULL) == (list->tail == NULL));
if (!list->head) return;
GPR_ASSERT(list->head->prev == NULL);
GPR_ASSERT(list->tail->next == NULL);
GPR_ASSERT((list->head == list->tail) == (list->head->next == NULL));
for (l = list->head; l; l = l->next) {
GPR_ASSERT(l->md);
GPR_ASSERT((l->prev == NULL) == (l == list->head));
GPR_ASSERT((l->next == NULL) == (l == list->tail));
if (l->next) GPR_ASSERT(l->next->prev == l);
if (l->prev) GPR_ASSERT(l->prev->next == l);
}
#endif /* NDEBUG */
}
#ifndef NDEBUG
void grpc_metadata_batch_assert_ok(grpc_metadata_batch *comd) {
assert_valid_list(&comd->list);
assert_valid_list(&comd->garbage);
}
#endif /* NDEBUG */
void grpc_metadata_batch_init(grpc_metadata_batch *comd) {
comd->list.head = comd->list.tail = comd->garbage.head = comd->garbage.tail =
NULL;
comd->deadline = gpr_inf_future;
}
void grpc_metadata_batch_destroy(grpc_metadata_batch *comd) {
grpc_linked_mdelem *l;
for (l = comd->list.head; l; l = l->next) {
grpc_mdelem_unref(l->md);
}
for (l = comd->garbage.head; l; l = l->next) {
grpc_mdelem_unref(l->md);
}
}
void grpc_metadata_batch_add_head(grpc_metadata_batch *comd,
grpc_linked_mdelem *storage,
grpc_mdelem *elem_to_add) {
GPR_ASSERT(elem_to_add);
storage->md = elem_to_add;
grpc_metadata_batch_link_head(comd, storage);
}
static void link_head(grpc_mdelem_list *list, grpc_linked_mdelem *storage) {
assert_valid_list(list);
GPR_ASSERT(storage->md);
storage->prev = NULL;
storage->next = list->head;
if (list->head != NULL) {
list->head->prev = storage;
} else {
list->tail = storage;
}
list->head = storage;
assert_valid_list(list);
}
void grpc_metadata_batch_link_head(grpc_metadata_batch *comd,
grpc_linked_mdelem *storage) {
link_head(&comd->list, storage);
}
void grpc_metadata_batch_add_tail(grpc_metadata_batch *comd,
grpc_linked_mdelem *storage,
grpc_mdelem *elem_to_add) {
GPR_ASSERT(elem_to_add);
storage->md = elem_to_add;
grpc_metadata_batch_link_tail(comd, storage);
}
static void link_tail(grpc_mdelem_list *list, grpc_linked_mdelem *storage) {
assert_valid_list(list);
GPR_ASSERT(storage->md);
storage->prev = list->tail;
storage->next = NULL;
if (list->tail != NULL) {
list->tail->next = storage;
} else {
list->head = storage;
}
list->tail = storage;
assert_valid_list(list);
}
void grpc_metadata_batch_link_tail(grpc_metadata_batch *comd,
grpc_linked_mdelem *storage) {
link_tail(&comd->list, storage);
}
void grpc_metadata_batch_merge(grpc_metadata_batch *target,
grpc_metadata_batch *add) {
grpc_linked_mdelem *l;
grpc_linked_mdelem *next;
for (l = add->list.head; l; l = next) {
next = l->next;
link_tail(&target->list, l);
}
for (l = add->garbage.head; l; l = next) {
next = l->next;
link_tail(&target->garbage, l);
}
}
void grpc_metadata_batch_filter(grpc_metadata_batch *comd,
grpc_mdelem *(*filter)(void *user_data,
grpc_mdelem *elem),
void *user_data) {
grpc_linked_mdelem *l;
grpc_linked_mdelem *next;
assert_valid_list(&comd->list);
assert_valid_list(&comd->garbage);
for (l = comd->list.head; l; l = next) {
grpc_mdelem *orig = l->md;
grpc_mdelem *filt = filter(user_data, orig);
next = l->next;
if (filt == NULL) {
if (l->prev) {
l->prev->next = l->next;
}
if (l->next) {
l->next->prev = l->prev;
}
if (comd->list.head == l) {
comd->list.head = l->next;
}
if (comd->list.tail == l) {
comd->list.tail = l->prev;
}
assert_valid_list(&comd->list);
link_head(&comd->garbage, l);
} else if (filt != orig) {
grpc_mdelem_unref(orig);
l->md = filt;
}
}
assert_valid_list(&comd->list);
assert_valid_list(&comd->garbage);
}

@ -50,8 +50,6 @@ typedef enum grpc_stream_op_code {
Must be ignored by receivers */
GRPC_NO_OP,
GRPC_OP_METADATA,
GRPC_OP_DEADLINE,
GRPC_OP_METADATA_BOUNDARY,
/* Begin a message/metadata element/status - as defined by
grpc_message_type. */
GRPC_OP_BEGIN_MESSAGE,
@ -76,6 +74,51 @@ typedef struct grpc_flow_ctl_cb {
void *arg;
} grpc_flow_ctl_cb;
typedef struct grpc_linked_mdelem {
grpc_mdelem *md;
struct grpc_linked_mdelem *next;
struct grpc_linked_mdelem *prev;
} grpc_linked_mdelem;
typedef struct grpc_mdelem_list {
grpc_linked_mdelem *head;
grpc_linked_mdelem *tail;
} grpc_mdelem_list;
typedef struct grpc_metadata_batch {
grpc_mdelem_list list;
grpc_mdelem_list garbage;
gpr_timespec deadline;
} grpc_metadata_batch;
void grpc_metadata_batch_init(grpc_metadata_batch *comd);
void grpc_metadata_batch_destroy(grpc_metadata_batch *comd);
void grpc_metadata_batch_merge(grpc_metadata_batch *target,
grpc_metadata_batch *add);
void grpc_metadata_batch_link_head(grpc_metadata_batch *comd,
grpc_linked_mdelem *storage);
void grpc_metadata_batch_link_tail(grpc_metadata_batch *comd,
grpc_linked_mdelem *storage);
void grpc_metadata_batch_add_head(grpc_metadata_batch *comd,
grpc_linked_mdelem *storage,
grpc_mdelem *elem_to_add);
void grpc_metadata_batch_add_tail(grpc_metadata_batch *comd,
grpc_linked_mdelem *storage,
grpc_mdelem *elem_to_add);
void grpc_metadata_batch_filter(grpc_metadata_batch *comd,
grpc_mdelem *(*filter)(void *user_data,
grpc_mdelem *elem),
void *user_data);
#ifndef NDEBUG
void grpc_metadata_batch_assert_ok(grpc_metadata_batch *comd);
#else
#define grpc_metadata_batch_assert_ok(comd) do {} while (0)
#endif
/* Represents a single operation performed on a stream/transport */
typedef struct grpc_stream_op {
/* the operation to be applied */
@ -84,8 +127,7 @@ typedef struct grpc_stream_op {
associated op-code */
union {
grpc_begin_message begin_message;
grpc_mdelem *metadata;
gpr_timespec deadline;
grpc_metadata_batch metadata;
gpr_slice slice;
grpc_flow_ctl_cb flow_ctl_cb;
} data;
@ -118,9 +160,7 @@ void grpc_sopb_add_no_op(grpc_stream_op_buffer *sopb);
/* Append a GRPC_OP_BEGIN to a buffer */
void grpc_sopb_add_begin_message(grpc_stream_op_buffer *sopb, gpr_uint32 length,
gpr_uint32 flags);
void grpc_sopb_add_metadata(grpc_stream_op_buffer *sopb, grpc_mdelem *metadata);
void grpc_sopb_add_deadline(grpc_stream_op_buffer *sopb, gpr_timespec deadline);
void grpc_sopb_add_metadata_boundary(grpc_stream_op_buffer *sopb);
void grpc_sopb_add_metadata(grpc_stream_op_buffer *sopb, grpc_metadata_batch metadata);
/* Append a GRPC_SLICE to a buffer - does not ref/unref the slice */
void grpc_sopb_add_slice(grpc_stream_op_buffer *sopb, gpr_slice slice);
/* Append a GRPC_OP_FLOW_CTL_CB to a buffer */

@ -40,6 +40,7 @@
#include <grpc/support/log.h>
#include <grpc/support/slice.h>
#include "src/core/profiling/timers.h"
#include "src/cpp/proto/proto_utils.h"
#include <grpc++/channel_arguments.h>
#include <grpc++/client_context.h>
@ -59,11 +60,17 @@ Channel::~Channel() { grpc_channel_destroy(c_channel_); }
Call Channel::CreateCall(const RpcMethod& method, ClientContext* context,
CompletionQueue* cq) {
auto c_call = grpc_channel_create_call(c_channel_, cq->cq(), method.name(),
context->authority().empty()
? target_.c_str()
: context->authority().c_str(),
context->raw_deadline());
auto c_call =
method.channel_tag()
? grpc_channel_create_registered_call(c_channel_, cq->cq(),
method.channel_tag(),
context->raw_deadline())
: grpc_channel_create_call(c_channel_, cq->cq(), method.name(),
context->authority().empty()
? target_.c_str()
: context->authority().c_str(),
context->raw_deadline());
GRPC_TIMER_MARK(CALL_CREATED, c_call);
context->set_call(c_call);
return Call(c_call, this, cq);
}
@ -72,9 +79,15 @@ void Channel::PerformOpsOnCall(CallOpBuffer* buf, Call* call) {
static const size_t MAX_OPS = 8;
size_t nops = MAX_OPS;
grpc_op ops[MAX_OPS];
GRPC_TIMER_MARK(PERFORM_OPS_BEGIN, call->call());
buf->FillOps(ops, &nops);
GPR_ASSERT(GRPC_CALL_OK ==
grpc_call_start_batch(call->call(), ops, nops, buf));
GRPC_TIMER_MARK(PERFORM_OPS_END, call->call());
}
void* Channel::RegisterMethod(const char* method) {
return grpc_channel_register_call(c_channel_, method, target_.c_str());
}
} // namespace grpc

@ -54,6 +54,7 @@ class Channel GRPC_FINAL : public ChannelInterface {
Channel(const grpc::string& target, grpc_channel* c_channel);
~Channel() GRPC_OVERRIDE;
virtual void *RegisterMethod(const char *method) GRPC_OVERRIDE;
virtual Call CreateCall(const RpcMethod& method, ClientContext* context,
CompletionQueue* cq) GRPC_OVERRIDE;
virtual void PerformOpsOnCall(CallOpBuffer* ops, Call* call) GRPC_OVERRIDE;

@ -39,13 +39,13 @@ namespace grpc {
// begin a call to a named method
std::unique_ptr<GenericClientAsyncReaderWriter> GenericStub::Call(
ClientContext* context, const grpc::string& method,
CompletionQueue* cq, void* tag) {
ClientContext* context, const grpc::string& method, CompletionQueue* cq,
void* tag) {
return std::unique_ptr<GenericClientAsyncReaderWriter>(
new GenericClientAsyncReaderWriter(
channel_.get(), cq, RpcMethod(method.c_str()), context, tag));
channel_.get(), cq,
RpcMethod(method.c_str(), RpcMethod::BIDI_STREAMING, nullptr),
context, tag));
}
} // namespace grpc
} // namespace grpc

@ -38,6 +38,7 @@
#include <grpc++/client_context.h>
#include <grpc++/channel_interface.h>
#include "src/core/profiling/timers.h"
#include "src/cpp/proto/proto_utils.h"
namespace grpc {
@ -231,11 +232,13 @@ void CallOpBuffer::FillOps(grpc_op* ops, size_t* nops) {
}
if (send_message_ || send_message_buffer_) {
if (send_message_) {
GRPC_TIMER_MARK(SER_PROTO_BEGIN, 0);
bool success = SerializeProto(*send_message_, &send_buf_);
if (!success) {
abort();
// TODO handle parse failure
}
GRPC_TIMER_MARK(SER_PROTO_END, 0);
} else {
send_buf_ = send_message_buffer_->buffer();
}
@ -307,8 +310,10 @@ bool CallOpBuffer::FinalizeResult(void** tag, bool* status) {
if (recv_buf_) {
got_message = *status;
if (recv_message_) {
GRPC_TIMER_MARK(DESER_PROTO_BEGIN, 0);
*status = *status && DeserializeProto(recv_buf_, recv_message_);
grpc_byte_buffer_destroy(recv_buf_);
GRPC_TIMER_MARK(DESER_PROTO_END, 0);
} else {
recv_message_buffer_->set_buffer(recv_buf_);
}

@ -68,9 +68,11 @@ bool AsyncServerContext::StartRead(grpc::protobuf::Message* request) {
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;
@ -87,7 +89,9 @@ bool AsyncServerContext::StartWriteStatus(const Status& status) {
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;
}

@ -47,6 +47,7 @@
#include <grpc++/thread_pool_interface.h>
#include <grpc++/time.h>
#include "src/core/profiling/timers.h"
#include "src/cpp/proto/proto_utils.h"
namespace grpc {
@ -123,10 +124,12 @@ class Server::SyncRequest GRPC_FINAL : public CompletionQueueTag {
std::unique_ptr<grpc::protobuf::Message> req;
std::unique_ptr<grpc::protobuf::Message> res;
if (has_request_payload_) {
GRPC_TIMER_MARK(DESER_PROTO_BEGIN, call_.call());
req.reset(method_->AllocateRequestProto());
if (!DeserializeProto(request_payload_, req.get())) {
abort(); // for now
}
GRPC_TIMER_MARK(DESER_PROTO_END, call_.call());
}
if (has_response_payload_) {
res.reset(method_->AllocateResponseProto());
@ -340,7 +343,9 @@ class Server::AsyncRequest GRPC_FINAL : public CompletionQueueTag {
bool orig_status = *status;
if (*status && request_) {
if (payload_) {
GRPC_TIMER_MARK(DESER_PROTO_BEGIN, call_);
*status = DeserializeProto(payload_, request_);
GRPC_TIMER_MARK(DESER_PROTO_END, call_);
} else {
*status = false;
}

@ -60,7 +60,6 @@ void ByteBuffer::Dump(std::vector<Slice>* slices) {
gpr_slice s;
while (grpc_byte_buffer_reader_next(reader, &s)) {
slices->push_back(Slice(s, Slice::STEAL_REF));
gpr_slice_unref(s);
}
grpc_byte_buffer_reader_destroy(reader);
}

@ -1,2 +1,3 @@
bin
obj
obj
*.nupkg

@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\grpc.dependencies.zlib.redist.1.2.8.9\build\portable-net45\grpc.dependencies.zlib.redist.props" Condition="Exists('..\packages\grpc.dependencies.zlib.redist.1.2.8.9\build\portable-net45\grpc.dependencies.zlib.redist.props')" />
<Import Project="..\packages\grpc.dependencies.openssl.redist.1.0.2.2\build\portable-net45\grpc.dependencies.openssl.redist.props" Condition="Exists('..\packages\grpc.dependencies.openssl.redist.1.0.2.2\build\portable-net45\grpc.dependencies.openssl.redist.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
@ -10,6 +12,7 @@
<RootNamespace>Grpc.Core</RootNamespace>
<AssemblyName>Grpc.Core</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<NuGetPackageImportStamp>8bb563fb</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -31,8 +34,9 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Collections.Immutable">
<HintPath>..\packages\System.Collections.Immutable.1.1.34-rc\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll</HintPath>
<Reference Include="System.Collections.Immutable, Version=1.0.34.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.Bcl.Immutable.1.0.34\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
@ -92,7 +96,7 @@
ignored, which gives us the desired effect. -->
<When Condition=" '$(OS)' != 'Unix' ">
<ItemGroup>
<Content Include="..\..\..\vsprojects\vs2013\Debug\grpc_csharp_ext.dll">
<Content Include="..\..\..\vsprojects\Debug\grpc_csharp_ext.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
@ -100,7 +104,16 @@
<Otherwise />
</Choose>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<Folder Include="Stub\" />
</ItemGroup>
<ItemGroup />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\grpc.dependencies.openssl.redist.1.0.2.2\build\portable-net45\grpc.dependencies.openssl.redist.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\grpc.dependencies.openssl.redist.1.0.2.2\build\portable-net45\grpc.dependencies.openssl.redist.props'))" />
<Error Condition="!Exists('..\packages\grpc.dependencies.openssl.redist.1.0.2.2\build\portable-net45\grpc.dependencies.openssl.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\grpc.dependencies.openssl.redist.1.0.2.2\build\portable-net45\grpc.dependencies.openssl.redist.targets'))" />
<Error Condition="!Exists('..\packages\grpc.dependencies.zlib.redist.1.2.8.9\build\portable-net45\grpc.dependencies.zlib.redist.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\grpc.dependencies.zlib.redist.1.2.8.9\build\portable-net45\grpc.dependencies.zlib.redist.props'))" />
<Error Condition="!Exists('..\packages\grpc.dependencies.zlib.redist.1.2.8.9\build\portable-net45\grpc.dependencies.zlib.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\grpc.dependencies.zlib.redist.1.2.8.9\build\portable-net45\grpc.dependencies.zlib.redist.targets'))" />
</Target>
<Import Project="..\packages\grpc.dependencies.openssl.redist.1.0.2.2\build\portable-net45\grpc.dependencies.openssl.redist.targets" Condition="Exists('..\packages\grpc.dependencies.openssl.redist.1.0.2.2\build\portable-net45\grpc.dependencies.openssl.redist.targets')" />
<Import Project="..\packages\grpc.dependencies.zlib.redist.1.2.8.9\build\portable-net45\grpc.dependencies.zlib.redist.targets" Condition="Exists('..\packages\grpc.dependencies.zlib.redist.1.2.8.9\build\portable-net45\grpc.dependencies.zlib.redist.targets')" />
</Project>

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<package >
<package>
<metadata>
<id>Grpc.Core</id>
<title>gRPC Core</title>
@ -7,7 +7,7 @@
<description>Core C# implementation of gRPC - an RPC library and framework. See project site for more info.
This is an experimental release, not ready to use.
</description>
<version>0.2.0</version>
<version>0.2.1</version>
<authors>Google Inc.</authors>
<owners>jtattermusch</owners>
<licenseUrl>https://github.com/grpc/grpc/blob/master/LICENSE</licenseUrl>
@ -16,6 +16,10 @@
<releaseNotes>The first experimental release. Not ready to use.</releaseNotes>
<copyright>Copyright 2015, Google Inc.</copyright>
<tags>gRPC RPC Protocol HTTP/2</tags>
<dependencies>
<dependency id="Microsoft.Bcl.Immutable" version="1.0.34" />
<dependency id="grpc.native.csharp_ext" version="0.6.0.0" />
</dependencies>
</metadata>
<files>
<file src="bin/Release/Grpc.Core.dll" target="lib/net45" />

@ -1,4 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="System.Collections.Immutable" version="1.1.34-rc" targetFramework="net45" />
<package id="grpc.dependencies.openssl.redist" version="1.0.2.2" targetFramework="net45" />
<package id="grpc.dependencies.zlib.redist" version="1.2.8.9" targetFramework="net45" />
<package id="Microsoft.Bcl.Immutable" version="1.0.34" targetFramework="net45" />
</packages>

@ -46,7 +46,7 @@ namespace math
Server server = new Server();
server.AddServiceDefinition(MathGrpc.BindService(new MathServiceImpl()));
int port = server.AddListeningPort(host + ":0");
int port = server.AddListeningPort(host + ":23456");
server.Start();
Console.WriteLine("MathServer listening on port " + port);

@ -39,8 +39,9 @@
<Reference Include="Google.ProtocolBuffers">
<HintPath>..\packages\Google.ProtocolBuffers.2.4.1.521\lib\net40\Google.ProtocolBuffers.dll</HintPath>
</Reference>
<Reference Include="System.Collections.Immutable">
<HintPath>..\packages\System.Collections.Immutable.1.1.34-rc\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll</HintPath>
<Reference Include="System.Collections.Immutable, Version=1.0.34.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.Bcl.Immutable.1.0.34\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Google.ProtocolBuffers" version="2.4.1.521" targetFramework="net45" />
<package id="Microsoft.Bcl.Immutable" version="1.0.34" targetFramework="net45" />
<package id="NUnit" version="2.6.4" targetFramework="net45" />
<package id="System.Collections.Immutable" version="1.1.34-rc" targetFramework="net45" />
</packages>

@ -14,6 +14,12 @@ This requires `node` to be installed. If you instead have the `nodejs` executabl
2. Follow the instructions in the `INSTALL` file in the root of that repository to install the C core library that this package depends on.
3. Run `npm install`.
If you install the gRPC C core library in a custom location, then you need to set some environment variables to install this library. The command will look like this:
```sh
CXXFLAGS=-I<custom location>/include LDFLAGS=-L<custom location>/lib npm install [grpc]
```
## Tests
To run the test suite, simply run `npm test` in the install location.

@ -33,10 +33,6 @@
'use strict';
var util = require('util');
var Transform = require('stream').Transform;
var grpc = require('..');
var math = grpc.load(__dirname + '/math.proto').math;
@ -54,11 +50,12 @@ function mathDiv(call, cb) {
// Unary + is explicit coersion to integer
if (+req.divisor === 0) {
cb(new Error('cannot divide by zero'));
} else {
cb(null, {
quotient: req.dividend / req.divisor,
remainder: req.dividend % req.divisor
});
}
cb(null, {
quotient: req.dividend / req.divisor,
remainder: req.dividend % req.divisor
});
}
/**
@ -97,24 +94,19 @@ function mathSum(call, cb) {
}
function mathDivMany(stream) {
// Here, call is a standard duplex Node object Stream
util.inherits(DivTransform, Transform);
function DivTransform() {
var options = {objectMode: true};
Transform.call(this, options);
}
DivTransform.prototype._transform = function(div_args, encoding, callback) {
stream.on('data', function(div_args) {
if (+div_args.divisor === 0) {
callback(new Error('cannot divide by zero'));
stream.emit('error', new Error('cannot divide by zero'));
} else {
stream.write({
quotient: div_args.dividend / div_args.divisor,
remainder: div_args.dividend % div_args.divisor
});
}
callback(null, {
quotient: div_args.dividend / div_args.divisor,
remainder: div_args.dividend % div_args.divisor
});
};
var transform = new DivTransform();
stream.pipe(transform);
transform.pipe(stream);
});
stream.on('end', function() {
stream.end();
});
}
var server = new Server({

@ -1,44 +0,0 @@
// This file will be moved to a new location.
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto2";
package proto2;
// An empty message that you can re-use to avoid defining duplicated empty
// messages in your project. A typical example is to use it as argument or the
// return value of a service API. For instance:
//
// service Foo {
// rpc Bar (proto2.Empty) returns (proto2.Empty) { };
// };
//
message Empty {}

@ -1,79 +0,0 @@
// This file will be moved to a new location.
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Labels provide a way to associate user-defined metadata with various
// objects. Labels may be used to organize objects into non-hierarchical
// groups; think metadata tags attached to mp3s.
syntax = "proto2";
package tech.label;
// A key-value pair applied to a given object.
message Label {
// The key of a label is a syntactically valid URL (as per RFC 1738) with
// the "scheme" and initial slashes omitted and with the additional
// restrictions noted below. Each key should be globally unique. The
// "host" portion is called the "namespace" and is not necessarily
// resolvable to a network endpoint. Instead, the namespace indicates what
// system or entity defines the semantics of the label. Namespaces do not
// restrict the set of objects to which a label may be associated.
//
// Keys are defined by the following grammar:
//
// key = hostname "/" kpath
// kpath = ksegment *[ "/" ksegment ]
// ksegment = alphadigit | *[ alphadigit | "-" | "_" | "." ]
//
// where "hostname" and "alphadigit" are defined as in RFC 1738.
//
// Example key:
// spanner.google.com/universe
required string key = 1;
// The value of the label.
oneof value {
// A string value.
string str_value = 2;
// An integer value.
int64 num_value = 3;
}
}
// A collection of labels, such as the set of all labels attached to an
// object. Each label in the set must have a different key.
//
// Users should prefer to embed "repeated Label" directly when possible.
// This message should only be used in cases where that isn't possible (e.g.
// with oneof).
message Labels {
repeated Label label = 1;
}

@ -1,734 +0,0 @@
// This file will be moved to a new location.
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Specification of the Pubsub API.
syntax = "proto2";
import "empty.proto";
import "label.proto";
package tech.pubsub;
// -----------------------------------------------------------------------------
// Overview of the Pubsub API
// -----------------------------------------------------------------------------
// This file describes an API for a Pubsub system. This system provides a
// reliable many-to-many communication mechanism between independently written
// publishers and subscribers where the publisher publishes messages to "topics"
// and each subscriber creates a "subscription" and consumes messages from it.
//
// (a) The pubsub system maintains bindings between topics and subscriptions.
// (b) A publisher publishes messages into a topic.
// (c) The pubsub system delivers messages from topics into relevant
// subscriptions.
// (d) A subscriber receives pending messages from its subscription and
// acknowledges or nacks each one to the pubsub system.
// (e) The pubsub system removes acknowledged messages from that subscription.
// -----------------------------------------------------------------------------
// Data Model
// -----------------------------------------------------------------------------
// The data model consists of the following:
//
// * Topic: A topic is a resource to which messages are published by publishers.
// Topics are named, and the name of the topic is unique within the pubsub
// system.
//
// * Subscription: A subscription records the subscriber's interest in a topic.
// It can optionally include a query to select a subset of interesting
// messages. The pubsub system maintains a logical cursor tracking the
// matching messages which still need to be delivered and acked so that
// they can retried as needed. The set of messages that have not been
// acknowledged is called the subscription backlog.
//
// * Message: A message is a unit of data that flows in the system. It contains
// opaque data from the publisher along with its labels.
//
// * Message Labels (optional): A set of opaque key, value pairs assigned
// by the publisher which the subscriber can use for filtering out messages
// in the topic. For example, a label with key "foo.com/device_type" and
// value "mobile" may be added for messages that are only relevant for a
// mobile subscriber; a subscriber on a phone may decide to create a
// subscription only for messages that have this label.
// -----------------------------------------------------------------------------
// Publisher Flow
// -----------------------------------------------------------------------------
// A publisher publishes messages to the topic using the Publish request:
//
// PubsubMessage message;
// message.set_data("....");
// Label label;
// label.set_key("foo.com/key1");
// label.set_str_value("value1");
// message.add_label(label);
// PublishRequest request;
// request.set_topic("topicName");
// request.set_message(message);
// PublisherService.Publish(request);
// -----------------------------------------------------------------------------
// Subscriber Flow
// -----------------------------------------------------------------------------
// The subscriber part of the API is richer than the publisher part and has a
// number of concepts w.r.t. subscription creation and monitoring:
//
// (1) A subscriber creates a subscription using the CreateSubscription call.
// It may specify an optional "query" to indicate that it wants to receive
// only messages with a certain set of labels using the label query syntax.
// It may also specify an optional truncation policy to indicate when old
// messages from the subcription can be removed.
//
// (2) A subscriber receives messages in one of two ways: via push or pull.
//
// (a) To receive messages via push, the PushConfig field must be specified in
// the Subscription parameter when creating a subscription. The PushConfig
// specifies an endpoint at which the subscriber must expose the
// PushEndpointService. Messages are received via the HandlePubsubEvent
// method. The push subscriber responds to the HandlePubsubEvent method
// with a result code that indicates one of three things: Ack (the message
// has been successfully processed and the Pubsub system may delete it),
// Nack (the message has been rejected, the Pubsub system should resend it
// at a later time), or Push-Back (this is a Nack with the additional
// semantics that the subscriber is overloaded and the pubsub system should
// back off on the rate at which it is invoking HandlePubsubEvent). The
// endpoint may be a load balancer for better scalability.
//
// (b) To receive messages via pull a subscriber calls the Pull method on the
// SubscriberService to get messages from the subscription. For each
// individual message, the subscriber may use the ack_id received in the
// PullResponse to Ack the message, Nack the message, or modify the ack
// deadline with ModifyAckDeadline. See the
// Subscription.ack_deadline_seconds field documentation for details on the
// ack deadline behavior.
//
// Note: Messages may be consumed in parallel by multiple subscribers making
// Pull calls to the same subscription; this will result in the set of
// messages from the subscription being shared and each subscriber
// receiving a subset of the messages.
//
// (4) The subscriber can explicitly truncate the current subscription.
//
// (5) "Truncated" events are delivered when a subscription is
// truncated, whether due to the subscription's truncation policy
// or an explicit request from the subscriber.
//
// Subscription creation:
//
// Subscription subscription;
// subscription.set_topic("topicName");
// subscription.set_name("subscriptionName");
// subscription.push_config().set_push_endpoint("machinename:8888");
// SubscriberService.CreateSubscription(subscription);
//
// Consuming messages via push:
//
// TODO(eschapira): Add HTTP push example.
//
// The port 'machinename:8888' must be bound to a stubby server that implements
// the PushEndpointService with the following method:
//
// int HandlePubsubEvent(PubsubEvent event) {
// if (event.subscription().equals("subscriptionName")) {
// if (event.has_message()) {
// Process(event.message().data());
// } else if (event.truncated()) {
// ProcessTruncatedEvent();
// }
// }
// return OK; // This return code implies an acknowledgment
// }
//
// Consuming messages via pull:
//
// The subscription must be created without setting the push_config field.
//
// PullRequest pull_request;
// pull_request.set_subscription("subscriptionName");
// pull_request.set_return_immediately(false);
// while (true) {
// PullResponse pull_response;
// if (SubscriberService.Pull(pull_request, pull_response) == OK) {
// PubsubEvent event = pull_response.pubsub_event();
// if (event.has_message()) {
// Process(event.message().data());
// } else if (event.truncated()) {
// ProcessTruncatedEvent();
// }
// AcknowledgeRequest ack_request;
// ackRequest.set_subscription("subscriptionName");
// ackRequest.set_ack_id(pull_response.ack_id());
// SubscriberService.Acknowledge(ack_request);
// }
// }
// -----------------------------------------------------------------------------
// Reliability Semantics
// -----------------------------------------------------------------------------
// When a subscriber successfully creates a subscription using
// Subscriber.CreateSubscription, it establishes a "subscription point" with
// respect to that subscription - the subscriber is guaranteed to receive any
// message published after this subscription point that matches the
// subscription's query. Note that messages published before the Subscription
// point may or may not be delivered.
//
// If the system truncates the subscription according to the specified
// truncation policy, the system delivers a subscription status event with the
// "truncated" field set to true. We refer to such events as "truncation
// events". A truncation event:
//
// * Informs the subscriber that part of the subscription messages have been
// discarded. The subscriber may want to recover from the message loss, e.g.,
// by resyncing its state with its backend.
// * Establishes a new subscription point, i.e., the subscriber is guaranteed to
// receive all changes published after the trunction event is received (or
// until another truncation event is received).
//
// Note that messages are not delivered in any particular order by the pubsub
// system. Furthermore, the system guarantees at-least-once delivery
// of each message or truncation events until acked.
// -----------------------------------------------------------------------------
// Deletion
// -----------------------------------------------------------------------------
// Both topics and subscriptions may be deleted. Deletion of a topic implies
// deletion of all attached subscriptions.
//
// When a subscription is deleted directly by calling DeleteSubscription, all
// messages are immediately dropped. If it is a pull subscriber, future pull
// requests will return NOT_FOUND.
//
// When a topic is deleted all corresponding subscriptions are immediately
// deleted, and subscribers experience the same behavior as directly deleting
// the subscription.
// -----------------------------------------------------------------------------
// The Publisher service and its protos.
// -----------------------------------------------------------------------------
// The service that an application uses to manipulate topics, and to send
// messages to a topic.
service PublisherService {
// Creates the given topic with the given name.
rpc CreateTopic(Topic) returns (Topic) {
}
// Adds a message to the topic. Returns NOT_FOUND if the topic does not
// exist.
// (-- For different error code values returned via Stubby, see
// util/task/codes.proto. --)
rpc Publish(PublishRequest) returns (proto2.Empty) {
}
// Adds one or more messages to the topic. Returns NOT_FOUND if the topic does
// not exist.
rpc PublishBatch(PublishBatchRequest) returns (PublishBatchResponse) {
}
// Gets the configuration of a topic. Since the topic only has the name
// attribute, this method is only useful to check the existence of a topic.
// If other attributes are added in the future, they will be returned here.
rpc GetTopic(GetTopicRequest) returns (Topic) {
}
// Lists matching topics.
rpc ListTopics(ListTopicsRequest) returns (ListTopicsResponse) {
}
// Deletes the topic with the given name. All subscriptions to this topic
// are also deleted. Returns NOT_FOUND if the topic does not exist.
// After a topic is deleted, a new topic may be created with the same name.
rpc DeleteTopic(DeleteTopicRequest) returns (proto2.Empty) {
}
}
// A topic resource.
message Topic {
// Name of the topic.
optional string name = 1;
}
// A message data and its labels.
message PubsubMessage {
// The message payload.
optional bytes data = 1;
// Optional list of labels for this message. Keys in this collection must
// be unique.
//(-- TODO(eschapira): Define how key namespace may be scoped to the topic.--)
repeated tech.label.Label label = 2;
// ID of this message assigned by the server at publication time. Guaranteed
// to be unique within the topic. This value may be read by a subscriber
// that receives a PubsubMessage via a Pull call or a push delivery. It must
// not be populated by a publisher in a Publish call.
optional string message_id = 3;
}
// Request for the GetTopic method.
message GetTopicRequest {
// The name of the topic to get.
optional string topic = 1;
}
// Request for the Publish method.
message PublishRequest {
// The message in the request will be published on this topic.
optional string topic = 1;
// The message to publish.
optional PubsubMessage message = 2;
}
// Request for the PublishBatch method.
message PublishBatchRequest {
// The messages in the request will be published on this topic.
optional string topic = 1;
// The messages to publish.
repeated PubsubMessage messages = 2;
}
// Response for the PublishBatch method.
message PublishBatchResponse {
// The server-assigned ID of each published message, in the same order as
// the messages in the request. IDs are guaranteed to be unique within
// the topic.
repeated string message_ids = 1;
}
// Request for the ListTopics method.
message ListTopicsRequest {
// A valid label query expression.
//
optional string query = 1;
// Maximum number of topics to return.
// (-- If not specified or <= 0, the implementation will select a reasonable
// value. --)
optional int32 max_results = 2;
// The value obtained in the last <code>ListTopicsResponse</code>
// for continuation.
optional string page_token = 3;
}
// Response for the ListTopics method.
message ListTopicsResponse {
// The resulting topics.
repeated Topic topic = 1;
// If not empty, indicates that there are more topics that match the request,
// and this value should be passed to the next <code>ListTopicsRequest</code>
// to continue.
optional string next_page_token = 2;
}
// Request for the Delete method.
message DeleteTopicRequest {
// Name of the topic to delete.
optional string topic = 1;
}
// -----------------------------------------------------------------------------
// The Subscriber service and its protos.
// -----------------------------------------------------------------------------
// The service that an application uses to manipulate subscriptions and to
// consume messages from a subscription via the pull method.
service SubscriberService {
// Creates a subscription on a given topic for a given subscriber.
// If the subscription already exists, returns ALREADY_EXISTS.
// If the corresponding topic doesn't exist, returns NOT_FOUND.
//
// If the name is not provided in the request, the server will assign a random
// name for this subscription on the same project as the topic.
rpc CreateSubscription(Subscription) returns (Subscription) {
}
// Gets the configuration details of a subscription.
rpc GetSubscription(GetSubscriptionRequest) returns (Subscription) {
}
// Lists matching subscriptions.
rpc ListSubscriptions(ListSubscriptionsRequest)
returns (ListSubscriptionsResponse) {
}
// Deletes an existing subscription. All pending messages in the subscription
// are immediately dropped. Calls to Pull after deletion will return
// NOT_FOUND.
rpc DeleteSubscription(DeleteSubscriptionRequest) returns (proto2.Empty) {
}
// Removes all the pending messages in the subscription and releases the
// storage associated with them. Results in a truncation event to be sent to
// the subscriber. Messages added after this call returns are stored in the
// subscription as before.
rpc TruncateSubscription(TruncateSubscriptionRequest) returns (proto2.Empty) {
}
//
// Push subscriber calls.
//
// Modifies the <code>PushConfig</code> for a specified subscription.
// This method can be used to suspend the flow of messages to an endpoint
// by clearing the <code>PushConfig</code> field in the request. Messages
// will be accumulated for delivery even if no push configuration is
// defined or while the configuration is modified.
rpc ModifyPushConfig(ModifyPushConfigRequest) returns (proto2.Empty) {
}
//
// Pull Subscriber calls
//
// Pulls a single message from the server.
// If return_immediately is true, and no messages are available in the
// subscription, this method returns FAILED_PRECONDITION. The system is free
// to return an UNAVAILABLE error if no messages are available in a
// reasonable amount of time (to reduce system load).
rpc Pull(PullRequest) returns (PullResponse) {
}
// Pulls messages from the server. Returns an empty list if there are no
// messages available in the backlog. The system is free to return UNAVAILABLE
// if there are too many pull requests outstanding for the given subscription.
rpc PullBatch(PullBatchRequest) returns (PullBatchResponse) {
}
// Modifies the Ack deadline for a message received from a pull request.
rpc ModifyAckDeadline(ModifyAckDeadlineRequest) returns (proto2.Empty) {
}
// Acknowledges a particular received message: the Pub/Sub system can remove
// the given message from the subscription. Acknowledging a message whose
// Ack deadline has expired may succeed, but the message could have been
// already redelivered. Acknowledging a message more than once will not
// result in an error. This is only used for messages received via pull.
rpc Acknowledge(AcknowledgeRequest) returns (proto2.Empty) {
}
// Refuses processing a particular received message. The system will
// redeliver this message to some consumer of the subscription at some
// future time. This is only used for messages received via pull.
rpc Nack(NackRequest) returns (proto2.Empty) {
}
}
// A subscription resource.
message Subscription {
// Name of the subscription.
optional string name = 1;
// The name of the topic from which this subscription is receiving messages.
optional string topic = 2;
// If <code>query</code> is non-empty, only messages on the subscriber's
// topic whose labels match the query will be returned. Otherwise all
// messages on the topic will be returned.
//
optional string query = 3;
// The subscriber may specify requirements for truncating unacknowledged
// subscription entries. The system will honor the
// <code>CreateSubscription</code> request only if it can meet these
// requirements. If this field is not specified, messages are never truncated
// by the system.
optional TruncationPolicy truncation_policy = 4;
// Specifies which messages can be truncated by the system.
message TruncationPolicy {
oneof policy {
// If <code>max_bytes</code> is specified, the system is allowed to drop
// old messages to keep the combined size of stored messages under
// <code>max_bytes</code>. This is a hint; the system may keep more than
// this many bytes, but will make a best effort to keep the size from
// growing much beyond this parameter.
int64 max_bytes = 1;
// If <code>max_age_seconds</code> is specified, the system is allowed to
// drop messages that have been stored for at least this many seconds.
// This is a hint; the system may keep these messages, but will make a
// best effort to remove them when their maximum age is reached.
int64 max_age_seconds = 2;
}
}
// If push delivery is used with this subscription, this field is
// used to configure it.
optional PushConfig push_config = 5;
// For either push or pull delivery, the value is the maximum time after a
// subscriber receives a message before the subscriber should acknowledge or
// Nack the message. If the Ack deadline for a message passes without an
// Ack or a Nack, the Pub/Sub system will eventually redeliver the message.
// If a subscriber acknowledges after the deadline, the Pub/Sub system may
// accept the Ack, but it is possible that the message has been already
// delivered again. Multiple Acks to the message are allowed and will
// succeed.
//
// For push delivery, this value is used to set the request timeout for
// the call to the push endpoint.
//
// For pull delivery, this value is used as the initial value for the Ack
// deadline. It may be overridden for a specific pull request (message) with
// <code>ModifyAckDeadline</code>.
// While a message is outstanding (i.e. it has been delivered to a pull
// subscriber and the subscriber has not yet Acked or Nacked), the Pub/Sub
// system will not deliver that message to another pull subscriber
// (on a best-effort basis).
optional int32 ack_deadline_seconds = 6;
// If this parameter is set to n, the system is allowed to (but not required
// to) delete the subscription when at least n seconds have elapsed since the
// client presence was detected. (Presence is detected through any
// interaction using the subscription ID, including Pull(), Get(), or
// acknowledging a message.)
//
// If this parameter is not set, the subscription will stay live until
// explicitly deleted.
//
// Clients can detect such garbage collection when a Get call or a Pull call
// (for pull subscribers only) returns NOT_FOUND.
optional int64 garbage_collect_seconds = 7;
}
// Configuration for a push delivery endpoint.
message PushConfig {
// A URL locating the endpoint to which messages should be pushed.
// For example, a Webhook endpoint might use "https://example.com/push".
// (-- An Android application might use "gcm:<REGID>", where <REGID> is a
// GCM registration id allocated for pushing messages to the application. --)
optional string push_endpoint = 1;
}
// An event indicating a received message or truncation event.
message PubsubEvent {
// The subscription that received the event.
optional string subscription = 1;
oneof type {
// A received message.
PubsubMessage message = 2;
// Indicates that this subscription has been truncated.
bool truncated = 3;
// Indicates that this subscription has been deleted. (Note that pull
// subscribers will always receive NOT_FOUND in response in their pull
// request on the subscription, rather than seeing this boolean.)
bool deleted = 4;
}
}
// Request for the GetSubscription method.
message GetSubscriptionRequest {
// The name of the subscription to get.
optional string subscription = 1;
}
// Request for the ListSubscriptions method.
message ListSubscriptionsRequest {
// A valid label query expression.
// (-- Which labels are required or supported is implementation-specific.
// TODO(eschapira): This method must support to query by topic. We must
// define the key URI for the "topic" label. --)
optional string query = 1;
// Maximum number of subscriptions to return.
// (-- If not specified or <= 0, the implementation will select a reasonable
// value. --)
optional int32 max_results = 3;
// The value obtained in the last <code>ListSubscriptionsResponse</code>
// for continuation.
optional string page_token = 4;
}
// Response for the ListSubscriptions method.
message ListSubscriptionsResponse {
// The subscriptions that match the request.
repeated Subscription subscription = 1;
// If not empty, indicates that there are more subscriptions that match the
// request and this value should be passed to the next
// <code>ListSubscriptionsRequest</code> to continue.
optional string next_page_token = 2;
}
// Request for the TruncateSubscription method.
message TruncateSubscriptionRequest {
// The subscription that is being truncated.
optional string subscription = 1;
}
// Request for the DeleteSubscription method.
message DeleteSubscriptionRequest {
// The subscription to delete.
optional string subscription = 1;
}
// Request for the ModifyPushConfig method.
message ModifyPushConfigRequest {
// The name of the subscription.
optional string subscription = 1;
// An empty <code>push_config</code> indicates that the Pub/Sub system should
// pause pushing messages from the given subscription.
optional PushConfig push_config = 2;
}
// -----------------------------------------------------------------------------
// The protos used by a pull subscriber.
// -----------------------------------------------------------------------------
// Request for the Pull method.
message PullRequest {
// The subscription from which a message should be pulled.
optional string subscription = 1;
// If this is specified as true the system will respond immediately even if
// it is not able to return a message in the Pull response. Otherwise the
// system is allowed to wait until at least one message is available rather
// than returning FAILED_PRECONDITION. The client may cancel the request if
// it does not wish to wait any longer for the response.
optional bool return_immediately = 2;
}
// Either a <code>PubsubMessage</code> or a truncation event. One of these two
// must be populated.
message PullResponse {
// This ID must be used to acknowledge the received event or message.
optional string ack_id = 1;
// A pubsub message or truncation event.
optional PubsubEvent pubsub_event = 2;
}
// Request for the PullBatch method.
message PullBatchRequest {
// The subscription from which messages should be pulled.
optional string subscription = 1;
// If this is specified as true the system will respond immediately even if
// it is not able to return a message in the Pull response. Otherwise the
// system is allowed to wait until at least one message is available rather
// than returning no messages. The client may cancel the request if it does
// not wish to wait any longer for the response.
optional bool return_immediately = 2;
// The maximum number of PubsubEvents returned for this request. The Pub/Sub
// system may return fewer than the number of events specified.
optional int32 max_events = 3;
}
// Response for the PullBatch method.
message PullBatchResponse {
// Received Pub/Sub messages or status events. The Pub/Sub system will return
// zero messages if there are no more messages available in the backlog. The
// Pub/Sub system may return fewer than the max_events requested even if
// there are more messages available in the backlog.
repeated PullResponse pull_responses = 2;
}
// Request for the ModifyAckDeadline method.
message ModifyAckDeadlineRequest {
// The name of the subscription from which messages are being pulled.
optional string subscription = 1;
// The acknowledgment ID.
optional string ack_id = 2;
// The new Ack deadline. Must be >= 0.
optional int32 ack_deadline_seconds = 3;
}
// Request for the Acknowledge method.
message AcknowledgeRequest {
// The subscription whose message is being acknowledged.
optional string subscription = 1;
// The acknowledgment ID for the message being acknowledged. This was
// returned by the Pub/Sub system in the Pull response.
repeated string ack_id = 2;
}
// Request for the Nack method.
message NackRequest {
// The subscription whose message is being Nacked.
optional string subscription = 1;
// The acknowledgment ID for the message being refused. This was returned by
// the Pub/Sub system in the Pull response.
repeated string ack_id = 2;
}
// -----------------------------------------------------------------------------
// The service and protos used by a push subscriber.
// -----------------------------------------------------------------------------
// The service that a subscriber uses to handle messages sent via push
// delivery.
// This service is not currently exported for HTTP clients.
// TODO(eschapira): Explain HTTP subscribers.
service PushEndpointService {
// Sends a <code>PubsubMessage</code> or a subscription status event to a
// push endpoint.
// The push endpoint responds with an empty message and a code from
// util/task/codes.proto. The following codes have a particular meaning to the
// Pub/Sub system:
// OK - This is interpreted by Pub/Sub as Ack.
// ABORTED - This is intepreted by Pub/Sub as a Nack, without implying
// pushback for congestion control. The Pub/Sub system will
// retry this message at a later time.
// UNAVAILABLE - This is intepreted by Pub/Sub as a Nack, with the additional
// semantics of push-back. The Pub/Sub system will use an AIMD
// congestion control algorithm to backoff the rate of sending
// messages from this subscription.
// Any other code, or a failure to respond, will be interpreted in the same
// way as ABORTED; i.e. the system will retry the message at a later time to
// ensure reliable delivery.
rpc HandlePubsubEvent(PubsubEvent) returns (proto2.Empty);
}

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

Loading…
Cancel
Save