Merge github.com:grpc/grpc

changes/47/217547/1
Craig Tiller 10 years ago
commit 3babf7c5ca
  1. 7
      .editorconfig
  2. 11
      .gitignore
  3. 44
      INSTALL
  4. 13483
      Makefile
  5. 8
      README.md
  6. 256
      build.json
  7. 30
      examples/pubsub/README
  8. 2
      examples/pubsub/empty.proto
  9. 2
      examples/pubsub/label.proto
  10. 178
      examples/pubsub/main.cc
  11. 124
      examples/pubsub/publisher.cc
  12. 26
      examples/pubsub/publisher.h
  13. 71
      examples/pubsub/publisher_test.cc
  14. 6
      examples/pubsub/pubsub.proto
  15. 118
      examples/pubsub/subscriber.cc
  16. 68
      examples/pubsub/subscriber.h
  17. 159
      examples/pubsub/subscriber_test.cc
  18. 3
      include/grpc++/credentials.h
  19. 2
      include/grpc/byte_buffer_reader.h
  20. 203
      include/grpc/grpc.h
  21. 6
      include/grpc/grpc_security.h
  22. 53
      include/grpc/support/log_win32.h
  23. 57
      include/grpc/support/port_platform.h
  24. 2
      include/grpc/support/slice_buffer.h
  25. 13
      include/grpc/support/thd.h
  26. 29
      include/grpc/support/time.h
  27. 2
      src/core/channel/channel_args.c
  28. 19
      src/core/channel/channel_stack.c
  29. 47
      src/core/channel/client_channel.c
  30. 14
      src/core/channel/connected_channel.c
  31. 18
      src/core/channel/http_client_filter.c
  32. 20
      src/core/channel/http_server_filter.c
  33. 2
      src/core/httpcli/format_request.c
  34. 15
      src/core/httpcli/httpcli_security_context.c
  35. 4
      src/core/iomgr/alarm.c
  36. 9
      src/core/iomgr/alarm_internal.h
  37. 120
      src/core/iomgr/fd_posix.c
  38. 26
      src/core/iomgr/fd_posix.h
  39. 200
      src/core/iomgr/iocp_windows.c
  40. 52
      src/core/iomgr/iocp_windows.h
  41. 10
      src/core/iomgr/iomgr.c
  42. 17
      src/core/iomgr/iomgr_posix.c
  43. 67
      src/core/iomgr/iomgr_windows.c
  44. 152
      src/core/iomgr/pollset_kick.c
  45. 10
      src/core/iomgr/pollset_kick.h
  46. 10
      src/core/iomgr/pollset_kick_posix.h
  47. 4
      src/core/iomgr/pollset_kick_windows.h
  48. 197
      src/core/iomgr/pollset_multipoller_with_epoll.c
  49. 35
      src/core/iomgr/pollset_multipoller_with_poll_posix.c
  50. 33
      src/core/iomgr/pollset_posix.c
  51. 7
      src/core/iomgr/pollset_posix.h
  52. 37
      src/core/iomgr/pollset_windows.c
  53. 2
      src/core/iomgr/pollset_windows.h
  54. 16
      src/core/iomgr/resolve_address.c
  55. 21
      src/core/iomgr/sockaddr_utils.c
  56. 6
      src/core/iomgr/sockaddr_utils.h
  57. 4
      src/core/iomgr/sockaddr_win32.h
  58. 3
      src/core/iomgr/socket_utils_linux.c
  59. 1
      src/core/iomgr/socket_utils_posix.c
  60. 77
      src/core/iomgr/socket_windows.c
  61. 67
      src/core/iomgr/socket_windows.h
  62. 6
      src/core/iomgr/tcp_client_posix.c
  63. 215
      src/core/iomgr/tcp_client_windows.c
  64. 2
      src/core/iomgr/tcp_server.h
  65. 29
      src/core/iomgr/tcp_server_posix.c
  66. 374
      src/core/iomgr/tcp_server_windows.c
  67. 373
      src/core/iomgr/tcp_windows.c
  68. 57
      src/core/iomgr/tcp_windows.h
  69. 82
      src/core/iomgr/wakeup_fd_eventfd.c
  70. 54
      src/core/iomgr/wakeup_fd_nospecial.c
  71. 97
      src/core/iomgr/wakeup_fd_pipe.c
  72. 14
      src/core/iomgr/wakeup_fd_pipe.h
  73. 76
      src/core/iomgr/wakeup_fd_posix.c
  74. 99
      src/core/iomgr/wakeup_fd_posix.h
  75. 42
      src/core/json/json.c
  76. 88
      src/core/json/json.h
  77. 49
      src/core/json/json_common.h
  78. 653
      src/core/json/json_reader.c
  79. 160
      src/core/json/json_reader.h
  80. 391
      src/core/json/json_string.c
  81. 252
      src/core/json/json_writer.c
  82. 93
      src/core/json/json_writer.h
  83. 130
      src/core/security/auth.c
  84. 3
      src/core/security/base64.c
  85. 55
      src/core/security/credentials.c
  86. 99
      src/core/security/json_token.c
  87. 4
      src/core/security/secure_endpoint.c
  88. 3
      src/core/security/secure_transport_setup.c
  89. 217
      src/core/security/security_context.c
  90. 38
      src/core/security/security_context.h
  91. 3
      src/core/security/server_secure_chttp2.c
  92. 4
      src/core/statistics/census_log.c
  93. 4
      src/core/statistics/census_rpc_stats.c
  94. 94
      src/core/statistics/census_tracing.c
  95. 47
      src/core/statistics/census_tracing.h
  96. 2
      src/core/statistics/hash_table.c
  97. 2
      src/core/statistics/window_stats.c
  98. 4
      src/core/support/cpu.h
  99. 40
      src/core/support/cpu_linux.c
  100. 4
      src/core/support/cpu_posix.c
  101. Some files were not shown because too many files have changed in this diff Show More

@ -0,0 +1,7 @@
root = true
[**]
end_of_line = LF
indent_style = space
indent_size = 2
insert_final_newline = true
tab_width = 8

11
.gitignore vendored

@ -17,8 +17,17 @@ coverage
# python compiled objects
*.pyc
#eclipse project files
.cproject
.project
.settings
# cache for run_tests.py
.run_tests_cache
# emacs temp files
*~
*~
# vim temp files
.*.swp

@ -14,10 +14,14 @@ A typical unix installation won't require any more steps than running:
$ make
# make install
You don't need anything else than GNU Make and gcc. Under a Debian or
Ubuntu system, this should boil down to the following package:
You don't need anything else than GNU Make, gcc and autotools. Under a Debian
or Ubuntu system, this should boil down to the following packages:
# apt-get install build-essential python-all-dev python-virtualenv
# apt-get install build-essential autoconf libtool
Building the python wrapper requires the following:
# apt-get install python-all-dev python-virtualenv
*******************************
@ -68,47 +72,43 @@ Compiling and running grpc plain-C tests dont't require any more dependency.
Compiling and running grpc C++ tests depend on protobuf 3.0.0, gtest and
gflags. Although gflags and protobuf are provided in third_party, you will
need to manually install these dependencies on your system to run these tests.
gflags. Although gflags is provided in third_party, you will need to manually
install that dependency on your system to run these tests.
Under a Debian or Ubuntu system, you can install the gtests and gflags packages
using apt-get:
# apt-get install libgflags-dev libgtest-dev
However, protobuf 3.0.0 isn't in a debian package yet: you'll need to compile
and install it from the sources in the third_party. Note that if you already
have the protobuf and protoc packages installed on your system, they will most
likely interfere, and you'll need to uninstall them first.
However, protobuf 3.0.0 isn't in a debian package yet, but the Makefile will
automatically try and compile the one present in third_party if you cloned the
repository recursively, and that it detects your system is lacking it.
Compiling and installing protobuf 3.0.0 requires a few more dependencies in
itself, notably the autoconf suite, curl, and unzip. If you have apt-get, you
can install these dependencies this way:
itself, notably the autoconf suite. If you have apt-get, you can install
these dependencies this way:
# apt-get install unzip curl autotools-dev
# apt-get install autoconf libtool
Then, you can build and install protobuf 3.0.0:
If you want to run the tests using one of the sanitized configurations, you
will need clang and its instrumented libc++:
$ cd third_party/protobuf
$ ./configure
$ make
# make install
# ldconfig
# apt-get install clang libc++-dev
A word on OpenSSL
-----------------
Secure HTTP2 requires to have the TLS extension ALPN (see rfc 7301 and
Secure HTTP2 requires the TLS extension ALPN (see rfc 7301 and
http://http2.github.io/http2-spec/ section 3.3). Our HTTP2 implementation
relies on OpenSSL's implementation. OpenSSL 1.0.2 is the first released version
of OpenSSL that has ALPN support, and this explains our dependency on it.
Note that the Makefile supports compiling only the unsecure elements of grpc,
and if you do not have OpenSSL and do not want it, you can still proceed
with installing only the elements you require. However, it is recommended
to encrypt your network traffic, therefore we urge you to not use the unsecure
version of grpc if possible.
with installing only the elements you require. However, we strongly recommend
the use of encryption for all network traffic, and discourage the use of grpc
without TLS.
Compiling

13483
Makefile

File diff suppressed because one or more lines are too long

@ -1,4 +1,4 @@
[gRPC - An RPC library and framework](http://github.com/google/grpc)
[gRPC - An RPC library and framework](http://github.com/grpc/grpc)
===================================
Copyright 2015 Google Inc.
@ -24,7 +24,7 @@ Developers using gRPC typically start with the description of an RPC service
(a collection of methods), and generate client and server side interfaces
which they use on the client-side and implement on the server side.
By default, gRPC uses [Protocol Buffers](github.com/google/protobuf) as the
By default, gRPC uses [Protocol Buffers](https://github.com/google/protobuf) as the
Interface Definition Language (IDL) for describing both the service interface
and the structure of the payload messages. It is possible to use other
alternatives if desired.
@ -67,7 +67,7 @@ fleshing out the details of each of the required operations.
A gRPC RPC comprises of a bidirectional stream of messages, initiated by the client. In the client-to-server direction, this stream begins with a mandatory `Call Header`, followed by optional `Initial-Metadata`, followed by zero or more `Payload Messages`. The server-to-client direction contains an optional `Initial-Metadata`, followed by zero or more `Payload Messages` terminated with a mandatory `Status` and optional `Status-Metadata` (a.k.a.,`Trailing-Metadata`).
## Implementation over HTTP/2
The abstract protocol defined above is implemented over [HTTP/2](https://http2.github.io/). gRPC bidirectional streams are mapped to HTTP/2 streams. The contents of `Call Header` and `Initial Metadata` are sent as HTTP/2 headers and subject to HPAC compression. `Payload Messages` are serialized into a byte stream of length prefixed gRPC frames which are then fragmented into HTTP/2 frames at the sender and reassembled at the receiver. `Status` and `Trailing-Metadata` are sent as HTTP/2 trailing headers (a.k.a., trailers).
The abstract protocol defined above is implemented over [HTTP/2](https://http2.github.io/). gRPC bidirectional streams are mapped to HTTP/2 streams. The contents of `Call Header` and `Initial Metadata` are sent as HTTP/2 headers and subject to HPACK compression. `Payload Messages` are serialized into a byte stream of length prefixed gRPC frames which are then fragmented into HTTP/2 frames at the sender and reassembled at the receiver. `Status` and `Trailing-Metadata` are sent as HTTP/2 trailing headers (a.k.a., trailers).
## Flow Control
gRPC inherits the flow control mchanims in HTTP/2 and uses them to enable fine-grained control of the amount of memory used for buffering in-flight messages.
gRPC inherits the flow control mechanisms in HTTP/2 and uses them to enable fine-grained control of the amount of memory used for buffering in-flight messages.

@ -42,6 +42,7 @@
"src/core/iomgr/endpoint.h",
"src/core/iomgr/endpoint_pair.h",
"src/core/iomgr/fd_posix.h",
"src/core/iomgr/iocp_windows.h",
"src/core/iomgr/iomgr.h",
"src/core/iomgr/iomgr_internal.h",
"src/core/iomgr/iomgr_posix.h",
@ -57,16 +58,25 @@
"src/core/iomgr/sockaddr_utils.h",
"src/core/iomgr/sockaddr_win32.h",
"src/core/iomgr/socket_utils_posix.h",
"src/core/iomgr/socket_windows.h",
"src/core/iomgr/tcp_client.h",
"src/core/iomgr/tcp_posix.h",
"src/core/iomgr/tcp_server.h",
"src/core/iomgr/tcp_windows.h",
"src/core/iomgr/time_averaged_stats.h",
"src/core/iomgr/wakeup_fd_pipe.h",
"src/core/iomgr/wakeup_fd_posix.h",
"src/core/json/json.h",
"src/core/json/json_common.h",
"src/core/json/json_reader.h",
"src/core/json/json_writer.h",
"src/core/statistics/census_interface.h",
"src/core/statistics/census_log.h",
"src/core/statistics/census_rpc_stats.h",
"src/core/statistics/census_tracing.h",
"src/core/statistics/hash_table.h",
"src/core/statistics/window_stats.h",
"src/core/surface/byte_buffer_queue.h",
"src/core/surface/call.h",
"src/core/surface/channel.h",
"src/core/surface/client.h",
@ -123,10 +133,13 @@
"src/core/iomgr/endpoint.c",
"src/core/iomgr/endpoint_pair_posix.c",
"src/core/iomgr/fd_posix.c",
"src/core/iomgr/iocp_windows.c",
"src/core/iomgr/iomgr.c",
"src/core/iomgr/iomgr_posix.c",
"src/core/iomgr/pollset_kick_posix.c",
"src/core/iomgr/iomgr_windows.c",
"src/core/iomgr/pollset_kick.c",
"src/core/iomgr/pollset_multipoller_with_poll_posix.c",
"src/core/iomgr/pollset_multipoller_with_epoll.c",
"src/core/iomgr/pollset_posix.c",
"src/core/iomgr/pollset_windows.c",
"src/core/iomgr/resolve_address.c",
@ -134,10 +147,22 @@
"src/core/iomgr/socket_utils_common_posix.c",
"src/core/iomgr/socket_utils_linux.c",
"src/core/iomgr/socket_utils_posix.c",
"src/core/iomgr/socket_windows.c",
"src/core/iomgr/tcp_client_posix.c",
"src/core/iomgr/tcp_client_windows.c",
"src/core/iomgr/tcp_posix.c",
"src/core/iomgr/tcp_server_posix.c",
"src/core/iomgr/tcp_server_windows.c",
"src/core/iomgr/tcp_windows.c",
"src/core/iomgr/time_averaged_stats.c",
"src/core/iomgr/wakeup_fd_eventfd.c",
"src/core/iomgr/wakeup_fd_nospecial.c",
"src/core/iomgr/wakeup_fd_pipe.c",
"src/core/iomgr/wakeup_fd_posix.c",
"src/core/json/json.c",
"src/core/json/json_reader.c",
"src/core/json/json_string.c",
"src/core/json/json_writer.c",
"src/core/statistics/census_init.c",
"src/core/statistics/census_log.c",
"src/core/statistics/census_rpc_stats.c",
@ -145,8 +170,10 @@
"src/core/statistics/hash_table.c",
"src/core/statistics/window_stats.c",
"src/core/surface/byte_buffer.c",
"src/core/surface/byte_buffer_queue.c",
"src/core/surface/byte_buffer_reader.c",
"src/core/surface/call.c",
"src/core/surface/call_details.c",
"src/core/surface/channel.c",
"src/core/surface/channel_create.c",
"src/core/surface/client.c",
@ -154,6 +181,7 @@
"src/core/surface/event_string.c",
"src/core/surface/init.c",
"src/core/surface/lame_client.c",
"src/core/surface/metadata_array.c",
"src/core/surface/secure_channel_create.c",
"src/core/surface/secure_server_create.c",
"src/core/surface/server.c",
@ -178,8 +206,7 @@
"src/core/transport/chttp2_transport.c",
"src/core/transport/metadata.c",
"src/core/transport/stream_op.c",
"src/core/transport/transport.c",
"third_party/cJSON/cJSON.c"
"src/core/transport/transport.c"
]
}
],
@ -199,6 +226,7 @@
"include/grpc/support/histogram.h",
"include/grpc/support/host_port.h",
"include/grpc/support/log.h",
"include/grpc/support/log_win32.h",
"include/grpc/support/port_platform.h",
"include/grpc/support/slice.h",
"include/grpc/support/slice_buffer.h",
@ -207,17 +235,16 @@
"include/grpc/support/sync_posix.h",
"include/grpc/support/sync_win32.h",
"include/grpc/support/thd.h",
"include/grpc/support/thd_posix.h",
"include/grpc/support/thd_win32.h",
"include/grpc/support/time.h",
"include/grpc/support/time_posix.h",
"include/grpc/support/time_win32.h",
"include/grpc/support/useful.h"
],
"headers": [
"src/core/support/cpu.h",
"src/core/support/env.h",
"src/core/support/file.h",
"src/core/support/murmur_hash.h",
"src/core/support/string.h",
"src/core/support/string_win32.h",
"src/core/support/thd_internal.h"
],
"src": [
@ -226,6 +253,13 @@
"src/core/support/cmdline.c",
"src/core/support/cpu_linux.c",
"src/core/support/cpu_posix.c",
"src/core/support/cpu_windows.c",
"src/core/support/env_linux.c",
"src/core/support/env_posix.c",
"src/core/support/env_win32.c",
"src/core/support/file.c",
"src/core/support/file_posix.c",
"src/core/support/file_win32.c",
"src/core/support/histogram.c",
"src/core/support/host_port.c",
"src/core/support/log.c",
@ -258,6 +292,9 @@
"src": [
"test/core/util/test_config.c"
],
"deps": [
"gpr"
],
"vs_project_guid": "{EAB0A629-17A9-44DB-B5FF-E91A721FE037}"
},
{
@ -305,6 +342,19 @@
"secure": true,
"vs_project_guid": "{29D16885-7228-4C31-81ED-5F9187C7F2A9}"
},
{
"name": "grpc_csharp_ext",
"build": "all",
"language": "c",
"src": [
"src/csharp/ext/grpc_csharp_ext.c"
],
"deps": [
"gpr",
"grpc"
],
"vs_project_guid": "{D64C6D63-4458-4A88-AB38-35678384A7E4}"
},
{
"name": "grpc_test_util",
"build": "private",
@ -323,6 +373,10 @@
"test/core/util/port_posix.c",
"test/core/util/slice_splitter.c"
],
"deps": [
"gpr",
"grpc"
],
"vs_project_guid": "{17BCAFC0-5FDC-4C94-AEB9-95F3E220614B}"
},
{
@ -379,11 +433,11 @@
"src/cpp/client/create_channel.cc",
"src/cpp/client/credentials.cc",
"src/cpp/client/internal_stub.cc",
"src/cpp/common/completion_queue.cc",
"src/cpp/common/rpc_method.cc",
"src/cpp/proto/proto_utils.cc",
"src/cpp/server/async_server.cc",
"src/cpp/server/async_server_context.cc",
"src/cpp/server/completion_queue.cc",
"src/cpp/server/server.cc",
"src/cpp/server/server_builder.cc",
"src/cpp/server/server_context_impl.cc",
@ -413,14 +467,15 @@
]
},
{
"name": "tips_client_lib",
"name": "pubsub_client_lib",
"build": "private",
"language": "c++",
"src": [
"examples/tips/label.proto",
"examples/tips/empty.proto",
"examples/tips/pubsub.proto",
"examples/tips/client.cc"
"examples/pubsub/label.proto",
"examples/pubsub/empty.proto",
"examples/pubsub/pubsub.proto",
"examples/pubsub/publisher.cc",
"examples/pubsub/subscriber.cc"
],
"deps": [
"grpc++",
@ -713,6 +768,7 @@
{
"name": "echo_client",
"build": "test",
"run": false,
"language": "c",
"src": [
"test/core/echo/client.c"
@ -722,12 +778,12 @@
"grpc",
"gpr_test_util",
"gpr"
],
"run": false
]
},
{
"name": "echo_server",
"build": "test",
"run": false,
"language": "c",
"src": [
"test/core/echo/server.c"
@ -737,8 +793,7 @@
"grpc",
"gpr_test_util",
"gpr"
],
"run": false
]
},
{
"name": "echo_test",
@ -771,6 +826,7 @@
{
"name": "fling_client",
"build": "test",
"run": false,
"language": "c",
"src": [
"test/core/fling/client.c"
@ -780,12 +836,12 @@
"grpc",
"gpr_test_util",
"gpr"
],
"run": false
]
},
{
"name": "fling_server",
"build": "test",
"run": false,
"language": "c",
"src": [
"test/core/fling/server.c"
@ -795,8 +851,7 @@
"grpc",
"gpr_test_util",
"gpr"
],
"run": false
]
},
{
"name": "fling_stream_test",
@ -863,6 +918,30 @@
"gpr"
]
},
{
"name": "gpr_env_test",
"build": "test",
"language": "c",
"src": [
"test/core/support/env_test.c"
],
"deps": [
"gpr_test_util",
"gpr"
]
},
{
"name": "gpr_file_test",
"build": "test",
"language": "c",
"src": [
"test/core/support/file_test.c"
],
"deps": [
"gpr_test_util",
"gpr"
]
},
{
"name": "gpr_histogram_test",
"build": "test",
@ -1179,6 +1258,48 @@
"gpr"
]
},
{
"name": "json_rewrite",
"build": "test",
"run": false,
"language": "c",
"src": [
"test/core/json/json_rewrite.c"
],
"deps": [
"grpc",
"gpr"
]
},
{
"name": "json_rewrite_test",
"build": "test",
"run": false,
"language": "c",
"src": [
"test/core/json/json_rewrite_test.c"
],
"deps": [
"grpc_test_util",
"grpc",
"gpr_test_util",
"gpr"
]
},
{
"name": "json_test",
"build": "test",
"language": "c",
"src": [
"test/core/json/json_test.c"
],
"deps": [
"grpc_test_util",
"grpc",
"gpr_test_util",
"gpr"
]
},
{
"name": "lame_client_test",
"build": "test",
@ -1262,11 +1383,11 @@
]
},
{
"name": "poll_kick_test",
"name": "poll_kick_posix_test",
"build": "test",
"language": "c",
"src": [
"test/core/iomgr/poll_kick_test.c"
"test/core/iomgr/poll_kick_posix_test.c"
],
"deps": [
"grpc_test_util",
@ -1475,6 +1596,7 @@
{
"name": "interop_client",
"build": "test",
"run": false,
"language": "c++",
"src": [
"test/cpp/interop/empty.proto",
@ -1489,12 +1611,12 @@
"grpc",
"gpr_test_util",
"gpr"
],
"run": false
]
},
{
"name": "interop_server",
"build": "test",
"run": false,
"language": "c++",
"src": [
"test/cpp/interop/empty.proto",
@ -1509,19 +1631,18 @@
"grpc",
"gpr_test_util",
"gpr"
],
"run": false
]
},
{
"name": "tips_client",
"name": "qps_client",
"build": "test",
"run": false,
"language": "c++",
"src": [
"examples/tips/client_main.cc"
"test/cpp/qps/qpstest.proto",
"test/cpp/qps/client.cc"
],
"deps": [
"tips_client_lib",
"grpc++_test_util",
"grpc_test_util",
"grpc++",
@ -1531,14 +1652,15 @@
]
},
{
"name": "tips_client_test",
"name": "qps_server",
"build": "test",
"run": false,
"language": "c++",
"src": [
"examples/tips/client_test.cc"
"test/cpp/qps/qpstest.proto",
"test/cpp/qps/server.cc"
],
"deps": [
"tips_client_lib",
"grpc++_test_util",
"grpc_test_util",
"grpc++",
@ -1548,15 +1670,30 @@
]
},
{
"name": "qps_client",
"name": "ruby_plugin",
"build": "protoc",
"language": "c++",
"headers": [
"src/compiler/cpp_generator.h",
"src/compiler/cpp_generator_helpers-inl.h",
"src/compiler/cpp_generator_map-inl.h",
"src/compiler/cpp_generator_string-inl.h"
],
"src": [
"src/compiler/ruby_generator.cc",
"src/compiler/ruby_plugin.cc"
],
"deps": [],
"secure": false
},
{
"name": "status_test",
"build": "test",
"language": "c++",
"src": [
"test/cpp/qps/qpstest.proto",
"test/cpp/qps/client.cc"
"test/cpp/util/status_test.cc"
],
"deps": [
"grpc++_test_util",
"grpc_test_util",
"grpc++",
"grpc",
@ -1565,12 +1702,11 @@
]
},
{
"name": "qps_server",
"name": "sync_client_async_server_test",
"build": "test",
"language": "c++",
"src": [
"test/cpp/qps/qpstest.proto",
"test/cpp/qps/server.cc"
"test/cpp/end2end/sync_client_async_server_test.cc"
],
"deps": [
"grpc++_test_util",
@ -1582,30 +1718,31 @@
]
},
{
"name": "ruby_plugin",
"build": "protoc",
"name": "thread_pool_test",
"build": "test",
"language": "c++",
"headers": [
"src/compiler/cpp_generator.h",
"src/compiler/cpp_generator_helpers-inl.h",
"src/compiler/cpp_generator_map-inl.h",
"src/compiler/cpp_generator_string-inl.h"
],
"src": [
"src/compiler/ruby_generator.cc",
"src/compiler/ruby_plugin.cc"
"test/cpp/server/thread_pool_test.cc"
],
"deps": [],
"secure": false
"deps": [
"grpc_test_util",
"grpc++",
"grpc",
"gpr_test_util",
"gpr"
]
},
{
"name": "status_test",
"name": "pubsub_client",
"build": "test",
"run": false,
"language": "c++",
"src": [
"test/cpp/util/status_test.cc"
"examples/pubsub/main.cc"
],
"deps": [
"pubsub_client_lib",
"grpc++_test_util",
"grpc_test_util",
"grpc++",
"grpc",
@ -1614,13 +1751,14 @@
]
},
{
"name": "sync_client_async_server_test",
"name": "pubsub_publisher_test",
"build": "test",
"language": "c++",
"src": [
"test/cpp/end2end/sync_client_async_server_test.cc"
"examples/pubsub/publisher_test.cc"
],
"deps": [
"pubsub_client_lib",
"grpc++_test_util",
"grpc_test_util",
"grpc++",
@ -1630,13 +1768,15 @@
]
},
{
"name": "thread_pool_test",
"name": "pubsub_subscriber_test",
"build": "test",
"language": "c++",
"src": [
"test/cpp/server/thread_pool_test.cc"
"examples/pubsub/subscriber_test.cc"
],
"deps": [
"pubsub_client_lib",
"grpc++_test_util",
"grpc_test_util",
"grpc++",
"grpc",

@ -0,0 +1,30 @@
C++ Client implementation for Cloud Pub/Sub service
(https://developers.google.com/apis-explorer/#p/pubsub/v1beta1/).
"Google Cloud Pub/Sub" API needs to be enabled at
https://console.developers.google.com/project to open the access for a client.
Select the project name, select the "APIs" under "APIs & auth", and turn
on "Google Cloud Pub/Sub" API.
To run the client from Google Compute Engine (GCE), the GCE instance needs to
be created with scope "https://www.googleapis.com/auth/cloud-platform" as below:
gcloud compute instances create instance-name
--image debian-7 --scopes https://www.googleapis.com/auth/cloud-platform
Google TLS cert is required to run the client, which can be downloaded from
Chrome browser.
To run the client from GCE:
make pubsub_client
GRPC_DEFAULT_SSL_ROOTS_FILE_PATH="Google TLS cert" bins/opt/pubsub_client
--project_id="your project id"
A service account credential is required to run the client from other
environments, which can be generated as a JSON key file from
https://console.developers.google.com/project/. To run the client with a service
account credential:
GRPC_DEFAULT_SSL_ROOTS_FILE_PATH="Google TLS cert" bins/opt/pubsub_client
--project_id="your project id"
--service_account_key_file="absolute path to the JSON key file"

@ -1,3 +1,5 @@
// This file will be moved to a new location.
syntax = "proto2";
package proto2;

@ -1,3 +1,5 @@
// This file will be moved to a new location.
// 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.

@ -0,0 +1,178 @@
/*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <chrono>
#include <fstream>
#include <memory>
#include <sstream>
#include <string>
#include <thread>
#include <grpc/grpc.h>
#include <grpc/support/log.h>
#include <google/gflags.h>
#include <grpc++/channel_interface.h>
#include <grpc++/create_channel.h>
#include <grpc++/credentials.h>
#include <grpc++/status.h>
#include "examples/pubsub/publisher.h"
#include "examples/pubsub/subscriber.h"
#include "test/cpp/util/create_test_channel.h"
DEFINE_int32(server_port, 443, "Server port.");
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");
DEFINE_string(service_account_key_file, "",
"Path to service account json key file.");
DEFINE_string(oauth_scope,
"https://www.googleapis.com/auth/cloud-platform",
"Scope for OAuth tokens.");
namespace {
const char kTopic[] = "testtopics";
const char kSubscriptionName[] = "testsubscription";
const char kMessageData[] = "Test Data";
} // namespace
grpc::string GetServiceAccountJsonKey() {
grpc::string json_key;
if (json_key.empty()) {
std::ifstream json_key_file(FLAGS_service_account_key_file);
std::stringstream key_stream;
key_stream << json_key_file.rdbuf();
json_key = key_stream.str();
}
return json_key;
}
int main(int argc, char** argv) {
grpc_init();
google::ParseCommandLineFlags(&argc, &argv, true);
gpr_log(GPR_INFO, "Start PUBSUB client");
std::ostringstream ss;
std::unique_ptr<grpc::Credentials> creds;
if (FLAGS_service_account_key_file != "") {
grpc::string json_key = GetServiceAccountJsonKey();
creds = grpc::CredentialsFactory::ServiceAccountCredentials(
json_key, FLAGS_oauth_scope, std::chrono::hours(1));
} else {
creds = grpc::CredentialsFactory::ComputeEngineCredentials();
}
ss << FLAGS_server_host << ":" << FLAGS_server_port;
std::shared_ptr<grpc::ChannelInterface> channel(
grpc::CreateTestChannel(
ss.str(),
FLAGS_server_host,
true, // enable SSL
true, // use prod roots
creds));
grpc::examples::pubsub::Publisher publisher(channel);
grpc::examples::pubsub::Subscriber subscriber(channel);
GPR_ASSERT(FLAGS_project_id != "");
ss.str("");
ss << "/topics/" << FLAGS_project_id << "/" << kTopic;
grpc::string topic = ss.str();
ss.str("");
ss << FLAGS_project_id << "/" << kSubscriptionName;
grpc::string subscription_name = ss.str();
// Clean up test topic and subcription if they exist before.
grpc::string subscription_topic;
if (subscriber.GetSubscription(
subscription_name, &subscription_topic).IsOk()) {
subscriber.DeleteSubscription(subscription_name);
}
if (publisher.GetTopic(topic).IsOk()) publisher.DeleteTopic(topic);
grpc::Status s = publisher.CreateTopic(topic);
gpr_log(GPR_INFO, "Create topic returns code %d, %s",
s.code(), s.details().c_str());
GPR_ASSERT(s.IsOk());
s = publisher.GetTopic(topic);
gpr_log(GPR_INFO, "Get topic returns code %d, %s",
s.code(), s.details().c_str());
GPR_ASSERT(s.IsOk());
std::vector<grpc::string> topics;
s = publisher.ListTopics(FLAGS_project_id, &topics);
gpr_log(GPR_INFO, "List topic returns code %d, %s",
s.code(), s.details().c_str());
bool topic_found = false;
for (unsigned int i = 0; i < topics.size(); i++) {
if (topics[i] == topic) topic_found = true;
gpr_log(GPR_INFO, "topic: %s", topics[i].c_str());
}
GPR_ASSERT(s.IsOk());
GPR_ASSERT(topic_found);
s = subscriber.CreateSubscription(topic, subscription_name);
gpr_log(GPR_INFO, "create subscrption returns code %d, %s",
s.code(), s.details().c_str());
GPR_ASSERT(s.IsOk());
s = publisher.Publish(topic, kMessageData);
gpr_log(GPR_INFO, "Publish %s returns code %d, %s",
kMessageData, s.code(), s.details().c_str());
GPR_ASSERT(s.IsOk());
grpc::string data;
s = subscriber.Pull(subscription_name, &data);
gpr_log(GPR_INFO, "Pull %s", data.c_str());
s = subscriber.DeleteSubscription(subscription_name);
gpr_log(GPR_INFO, "Delete subscription returns code %d, %s",
s.code(), s.details().c_str());
GPR_ASSERT(s.IsOk());
s = publisher.DeleteTopic(topic);
gpr_log(GPR_INFO, "Delete topic returns code %d, %s",
s.code(), s.details().c_str());
GPR_ASSERT(s.IsOk());
subscriber.Shutdown();
publisher.Shutdown();
channel.reset();
grpc_shutdown();
return 0;
}

@ -0,0 +1,124 @@
/*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <sstream>
#include <grpc++/client_context.h>
#include "examples/pubsub/publisher.h"
using tech::pubsub::Topic;
using tech::pubsub::DeleteTopicRequest;
using tech::pubsub::GetTopicRequest;
using tech::pubsub::PublisherService;
using tech::pubsub::ListTopicsRequest;
using tech::pubsub::ListTopicsResponse;
using tech::pubsub::PublishRequest;
using tech::pubsub::PubsubMessage;
namespace grpc {
namespace examples {
namespace pubsub {
Publisher::Publisher(std::shared_ptr<ChannelInterface> channel)
: stub_(PublisherService::NewStub(channel)) {
}
void Publisher::Shutdown() {
stub_.reset();
}
Status Publisher::CreateTopic(const grpc::string& topic) {
Topic request;
Topic response;
request.set_name(topic);
ClientContext context;
return stub_->CreateTopic(&context, request, &response);
}
Status Publisher::ListTopics(const grpc::string& project_id,
std::vector<grpc::string>* topics) {
ListTopicsRequest request;
ListTopicsResponse response;
ClientContext context;
std::ostringstream ss;
ss << "cloud.googleapis.com/project in (/projects/" << project_id << ")";
request.set_query(ss.str());
Status s = stub_->ListTopics(&context, request, &response);
tech::pubsub::Topic topic;
for (int i = 0; i < response.topic_size(); i++) {
topic = response.topic(i);
topics->push_back(topic.name());
}
return s;
}
Status Publisher::GetTopic(const grpc::string& topic) {
GetTopicRequest request;
Topic response;
ClientContext context;
request.set_topic(topic);
return stub_->GetTopic(&context, request, &response);
}
Status Publisher::DeleteTopic(const grpc::string& topic) {
DeleteTopicRequest request;
proto2::Empty response;
ClientContext context;
request.set_topic(topic);
return stub_->DeleteTopic(&context, request, &response);
}
Status Publisher::Publish(const grpc::string& topic, const grpc::string& data) {
PublishRequest request;
proto2::Empty response;
ClientContext context;
request.mutable_message()->set_data(data);
request.set_topic(topic);
return stub_->Publish(&context, request, &response);
}
} // namespace pubsub
} // namespace examples
} // namespace grpc

@ -31,29 +31,37 @@
*
*/
#ifndef __GRPCPP_EXAMPLES_TIPS_CLIENT_H_
#define __GRPCPP_EXAMPLES_TIPS_CLIENT_H_
#ifndef __GRPCPP_EXAMPLES_PUBSUB_PUBLISHER_H_
#define __GRPCPP_EXAMPLES_PUBSUB_PUBLISHER_H_
#include <grpc++/channel_interface.h>
#include <grpc++/status.h>
#include "examples/tips/pubsub.pb.h"
#include "examples/pubsub/pubsub.pb.h"
namespace grpc {
namespace examples {
namespace tips {
namespace pubsub {
class Client {
class Publisher {
public:
Client(std::shared_ptr<grpc::ChannelInterface> channel);
Status CreateTopic(grpc::string topic);
Publisher(std::shared_ptr<ChannelInterface> channel);
void Shutdown();
Status CreateTopic(const grpc::string& topic);
Status GetTopic(const grpc::string& topic);
Status DeleteTopic(const grpc::string& topic);
Status ListTopics(const grpc::string& project_id,
std::vector<grpc::string>* topics);
Status Publish(const grpc::string& topic, const grpc::string& data);
private:
std::unique_ptr<tech::pubsub::PublisherService::Stub> stub_;
};
} // namespace tips
} // namespace pubsub
} // namespace examples
} // namespace grpc
#endif // __GRPCPP_EXAMPLES_TIPS_CLIENT_H_
#endif // __GRPCPP_EXAMPLES_PUBSUB_PUBLISHER_H_

@ -31,6 +31,8 @@
*
*/
#include <google/protobuf/stubs/common.h>
#include <grpc++/channel_arguments.h>
#include <grpc++/channel_interface.h>
#include <grpc++/client_context.h>
@ -41,7 +43,7 @@
#include <grpc++/status.h>
#include <gtest/gtest.h>
#include "examples/tips/client.h"
#include "examples/pubsub/publisher.h"
#include "test/core/util/port.h"
#include "test/core/util/test_config.h"
@ -51,9 +53,11 @@ namespace grpc {
namespace testing {
namespace {
const char kProjectId[] = "project id";
const char kTopic[] = "test topic";
const char kMessageData[] = "test message data";
class PublishServiceImpl : public tech::pubsub::PublisherService::Service {
class PublisherServiceImpl : public tech::pubsub::PublisherService::Service {
public:
Status CreateTopic(::grpc::ServerContext* context,
const ::tech::pubsub::Topic* request,
@ -61,34 +65,81 @@ class PublishServiceImpl : public tech::pubsub::PublisherService::Service {
EXPECT_EQ(request->name(), kTopic);
return Status::OK;
}
Status Publish(ServerContext* context,
const ::tech::pubsub::PublishRequest* request,
::proto2::Empty* response) override {
EXPECT_EQ(request->message().data(), kMessageData);
return Status::OK;
}
Status GetTopic(ServerContext* context,
const ::tech::pubsub::GetTopicRequest* request,
::tech::pubsub::Topic* response) override {
EXPECT_EQ(request->topic(), kTopic);
return Status::OK;
}
Status ListTopics(ServerContext* context,
const ::tech::pubsub::ListTopicsRequest* request,
::tech::pubsub::ListTopicsResponse* response) override {
std::ostringstream ss;
ss << "cloud.googleapis.com/project in (/projects/" << kProjectId << ")";
EXPECT_EQ(request->query(), ss.str());
response->add_topic()->set_name(kTopic);
return Status::OK;
}
Status DeleteTopic(ServerContext* context,
const ::tech::pubsub::DeleteTopicRequest* request,
::proto2::Empty* response) override {
EXPECT_EQ(request->topic(), kTopic);
return Status::OK;
}
};
class End2endTest : public ::testing::Test {
class PublisherTest : public ::testing::Test {
protected:
// Setup a server and a client for PublisherService.
void SetUp() override {
int port = grpc_pick_unused_port_or_die();
server_address_ << "localhost:" << port;
// Setup server
ServerBuilder builder;
builder.AddPort(server_address_.str());
builder.RegisterService(service_.service());
server_ = builder.BuildAndStart();
channel_ = CreateChannel(server_address_.str(), ChannelArguments());
publisher_.reset(new grpc::examples::pubsub::Publisher(channel_));
}
void TearDown() override { server_->Shutdown(); }
void TearDown() override {
server_->Shutdown();
publisher_->Shutdown();
}
std::unique_ptr<Server> server_;
std::ostringstream server_address_;
PublishServiceImpl service_;
std::unique_ptr<Server> server_;
PublisherServiceImpl service_;
std::shared_ptr<ChannelInterface> channel_;
std::unique_ptr<grpc::examples::pubsub::Publisher> publisher_;
};
TEST_F(End2endTest, CreateTopic) {
grpc::examples::tips::Client client(channel_);
client.CreateTopic(kTopic);
TEST_F(PublisherTest, TestPublisher) {
EXPECT_TRUE(publisher_->CreateTopic(kTopic).IsOk());
EXPECT_TRUE(publisher_->Publish(kTopic, kMessageData).IsOk());
EXPECT_TRUE(publisher_->GetTopic(kTopic).IsOk());
std::vector<grpc::string> topics;
EXPECT_TRUE(publisher_->ListTopics(kProjectId, &topics).IsOk());
EXPECT_EQ(topics.size(), 1);
EXPECT_EQ(topics[0], kTopic);
}
} // namespace

@ -1,9 +1,11 @@
// This file will be moved to a new location.
// Specification of the Pubsub API.
syntax = "proto2";
import "examples/tips/empty.proto";
import "examples/tips/label.proto";
import "examples/pubsub/empty.proto";
import "examples/pubsub/label.proto";
package tech.pubsub;

@ -0,0 +1,118 @@
/*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING 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++/client_context.h>
#include "examples/pubsub/subscriber.h"
using tech::pubsub::Topic;
using tech::pubsub::DeleteTopicRequest;
using tech::pubsub::GetTopicRequest;
using tech::pubsub::SubscriberService;
using tech::pubsub::ListTopicsRequest;
using tech::pubsub::ListTopicsResponse;
using tech::pubsub::PublishRequest;
using tech::pubsub::PubsubMessage;
namespace grpc {
namespace examples {
namespace pubsub {
Subscriber::Subscriber(std::shared_ptr<ChannelInterface> channel)
: stub_(SubscriberService::NewStub(channel)) {
}
void Subscriber::Shutdown() {
stub_.reset();
}
Status Subscriber::CreateSubscription(const grpc::string& topic,
const grpc::string& name) {
tech::pubsub::Subscription request;
tech::pubsub::Subscription response;
ClientContext context;
request.set_topic(topic);
request.set_name(name);
return stub_->CreateSubscription(&context, request, &response);
}
Status Subscriber::GetSubscription(const grpc::string& name,
grpc::string* topic) {
tech::pubsub::GetSubscriptionRequest request;
tech::pubsub::Subscription response;
ClientContext context;
request.set_subscription(name);
Status s = stub_->GetSubscription(&context, request, &response);
*topic = response.topic();
return s;
}
Status Subscriber::DeleteSubscription(const grpc::string& name) {
tech::pubsub::DeleteSubscriptionRequest request;
proto2::Empty response;
ClientContext context;
request.set_subscription(name);
return stub_->DeleteSubscription(&context, request, &response);
}
Status Subscriber::Pull(const grpc::string& name, grpc::string* data) {
tech::pubsub::PullRequest request;
tech::pubsub::PullResponse response;
ClientContext context;
request.set_subscription(name);
Status s = stub_->Pull(&context, request, &response);
if (s.IsOk()) {
tech::pubsub::PubsubEvent event = response.pubsub_event();
if (event.has_message()) {
*data = event.message().data();
}
tech::pubsub::AcknowledgeRequest ack;
proto2::Empty empty;
ClientContext ack_context;
ack.set_subscription(name);
ack.add_ack_id(response.ack_id());
stub_->Acknowledge(&ack_context, ack, &empty);
}
return s;
}
} // namespace pubsub
} // namespace examples
} // namespace grpc

@ -0,0 +1,68 @@
/*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef __GRPCPP_EXAMPLES_PUBSUB_SUBSCRIBER_H_
#define __GRPCPP_EXAMPLES_PUBSUB_SUBSCRIBER_H_
#include <grpc++/channel_interface.h>
#include <grpc++/status.h>
#include "examples/pubsub/pubsub.pb.h"
namespace grpc {
namespace examples {
namespace pubsub {
class Subscriber {
public:
Subscriber(std::shared_ptr<ChannelInterface> channel);
void Shutdown();
Status CreateSubscription(const grpc::string& topic,
const grpc::string& name);
Status GetSubscription(const grpc::string& name, grpc::string* topic);
Status DeleteSubscription(const grpc::string& name);
Status Pull(const grpc::string& name, grpc::string* data);
private:
std::unique_ptr<tech::pubsub::SubscriberService::Stub> stub_;
};
} // namespace pubsub
} // namespace examples
} // namespace grpc
#endif // __GRPCPP_EXAMPLES_PUBSUB_SUBSCRIBER_H_

@ -0,0 +1,159 @@
/*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <google/protobuf/stubs/common.h>
#include <grpc++/channel_arguments.h>
#include <grpc++/channel_interface.h>
#include <grpc++/client_context.h>
#include <grpc++/create_channel.h>
#include <grpc++/server.h>
#include <grpc++/server_builder.h>
#include <grpc++/server_context.h>
#include <grpc++/status.h>
#include <gtest/gtest.h>
#include "examples/pubsub/subscriber.h"
#include "test/core/util/port.h"
#include "test/core/util/test_config.h"
namespace grpc {
namespace testing {
namespace {
const char kTopic[] = "test topic";
const char kSubscriptionName[] = "subscription name";
const char kData[] = "Message data";
class SubscriberServiceImpl : public tech::pubsub::SubscriberService::Service {
public:
Status CreateSubscription(ServerContext* context,
const tech::pubsub::Subscription* request,
tech::pubsub::Subscription* response) override {
EXPECT_EQ(request->topic(), kTopic);
EXPECT_EQ(request->name(), kSubscriptionName);
return Status::OK;
}
Status GetSubscription(ServerContext* context,
const tech::pubsub::GetSubscriptionRequest* request,
tech::pubsub::Subscription* response) override {
EXPECT_EQ(request->subscription(), kSubscriptionName);
response->set_topic(kTopic);
return Status::OK;
}
Status DeleteSubscription(
ServerContext* context,
const tech::pubsub::DeleteSubscriptionRequest* request,
proto2::Empty* response) override {
EXPECT_EQ(request->subscription(), kSubscriptionName);
return Status::OK;
}
Status Pull(ServerContext* context,
const tech::pubsub::PullRequest* request,
tech::pubsub::PullResponse* response) override {
EXPECT_EQ(request->subscription(), kSubscriptionName);
response->set_ack_id("1");
response->mutable_pubsub_event()->mutable_message()->set_data(kData);
return Status::OK;
}
Status Acknowledge(ServerContext* context,
const tech::pubsub::AcknowledgeRequest* request,
proto2::Empty* response) override {
return Status::OK;
}
};
class SubscriberTest : public ::testing::Test {
protected:
// Setup a server and a client for SubscriberService.
void SetUp() override {
int port = grpc_pick_unused_port_or_die();
server_address_ << "localhost:" << port;
ServerBuilder builder;
builder.AddPort(server_address_.str());
builder.RegisterService(service_.service());
server_ = builder.BuildAndStart();
channel_ = CreateChannel(server_address_.str(), ChannelArguments());
subscriber_.reset(new grpc::examples::pubsub::Subscriber(channel_));
}
void TearDown() override {
server_->Shutdown();
subscriber_->Shutdown();
}
std::ostringstream server_address_;
std::unique_ptr<Server> server_;
SubscriberServiceImpl service_;
std::shared_ptr<ChannelInterface> channel_;
std::unique_ptr<grpc::examples::pubsub::Subscriber> subscriber_;
};
TEST_F(SubscriberTest, TestSubscriber) {
EXPECT_TRUE(subscriber_->CreateSubscription(kTopic,
kSubscriptionName).IsOk());
grpc::string topic;
EXPECT_TRUE(subscriber_->GetSubscription(kSubscriptionName,
&topic).IsOk());
EXPECT_EQ(topic, kTopic);
grpc::string data;
EXPECT_TRUE(subscriber_->Pull(kSubscriptionName,
&data).IsOk());
EXPECT_TRUE(subscriber_->DeleteSubscription(kSubscriptionName).IsOk());
}
} // namespace
} // namespace testing
} // namespace grpc
int main(int argc, char** argv) {
grpc_test_init(argc, argv);
grpc_init();
::testing::InitGoogleTest(&argc, argv);
gpr_log(GPR_INFO, "Start test ...");
int result = RUN_ALL_TESTS();
grpc_shutdown();
return result;
}

@ -66,14 +66,13 @@ class Credentials final {
// Options used to build SslCredentials
// pem_roots_cert is the buffer containing the PEM encoding of the server root
// certificates. This parameter cannot be empty.
// certificates. If this parameter is empty, the default roots will be used.
// pem_private_key is the buffer containing the PEM encoding of the client's
// private key. This parameter can be empty if the client does not have a
// private key.
// pem_cert_chain is the buffer containing the PEM encoding of the client's
// certificate chain. This parameter can be empty if the client does not have
// a certificate chain.
// TODO(jboeuf) Change it to point to a file.
struct SslCredentialsOptions {
grpc::string pem_root_certs;
grpc::string pem_private_key;

@ -42,7 +42,7 @@ struct grpc_byte_buffer_reader {
/* Different current objects correspond to different types of byte buffers */
union {
/* Index into a slice buffer's array of slices */
int index;
unsigned index;
} current;
};

@ -44,7 +44,7 @@
extern "C" {
#endif
/* Completion Channels enable notification of the completion of asynchronous
/* Completion Queues enable notification of the completion of asynchronous
actions. */
typedef struct grpc_completion_queue grpc_completion_queue;
@ -156,7 +156,8 @@ typedef enum grpc_op_error {
struct grpc_byte_buffer;
typedef struct grpc_byte_buffer grpc_byte_buffer;
/* Sample helpers to obtain byte buffers (these will certainly move place */
/* Sample helpers to obtain byte buffers (these will certainly move
someplace else) */
grpc_byte_buffer *grpc_byte_buffer_create(gpr_slice *slices, size_t nslices);
grpc_byte_buffer *grpc_byte_buffer_copy(grpc_byte_buffer *bb);
size_t grpc_byte_buffer_length(grpc_byte_buffer *bb);
@ -177,23 +178,22 @@ void grpc_byte_buffer_reader_destroy(grpc_byte_buffer_reader *reader);
/* A single metadata element */
typedef struct grpc_metadata {
char *key;
char *value;
const char *key;
const char *value;
size_t value_length;
} grpc_metadata;
typedef enum grpc_completion_type {
GRPC_QUEUE_SHUTDOWN, /* Shutting down */
GRPC_READ, /* A read has completed */
GRPC_INVOKE_ACCEPTED, /* An invoke call has been accepted by flow
control */
GRPC_WRITE_ACCEPTED, /* A write has been accepted by
flow control */
GRPC_QUEUE_SHUTDOWN, /* Shutting down */
GRPC_OP_COMPLETE, /* operation completion */
GRPC_READ, /* A read has completed */
GRPC_WRITE_ACCEPTED, /* A write has been accepted by
flow control */
GRPC_FINISH_ACCEPTED, /* writes_done or write_status has been accepted */
GRPC_CLIENT_METADATA_READ, /* The metadata array sent by server received at
client */
GRPC_FINISHED, /* An RPC has finished. The event contains status.
On the server this will be OK or Cancelled. */
GRPC_FINISHED, /* An RPC has finished. The event contains status.
On the server this will be OK or Cancelled. */
GRPC_SERVER_RPC_NEW, /* A new RPC has arrived at the server */
GRPC_SERVER_SHUTDOWN, /* The server has finished shutting down */
GRPC_COMPLETION_DO_NOT_USE /* must be last, forces users to include
@ -213,6 +213,7 @@ typedef struct grpc_event {
grpc_op_error write_accepted;
grpc_op_error finish_accepted;
grpc_op_error invoke_accepted;
grpc_op_error op_complete;
struct {
size_t count;
grpc_metadata *elements;
@ -233,15 +234,119 @@ typedef struct grpc_event {
} data;
} grpc_event;
typedef struct {
size_t count;
size_t capacity;
grpc_metadata *metadata;
} grpc_metadata_array;
void grpc_metadata_array_init(grpc_metadata_array *array);
void grpc_metadata_array_destroy(grpc_metadata_array *array);
typedef struct {
char *method;
size_t method_capacity;
char *host;
size_t host_capacity;
gpr_timespec deadline;
} grpc_call_details;
void grpc_call_details_init(grpc_call_details *details);
void grpc_call_details_destroy(grpc_call_details *details);
typedef enum {
/* Send initial metadata: one and only one instance MUST be sent for each call,
unless the call was cancelled - in which case this can be skipped */
GRPC_OP_SEND_INITIAL_METADATA = 0,
/* Send a message: 0 or more of these operations can occur for each call */
GRPC_OP_SEND_MESSAGE,
/* Send a close from the server: one and only one instance MUST be sent from the client,
unless the call was cancelled - in which case this can be skipped */
GRPC_OP_SEND_CLOSE_FROM_CLIENT,
/* Send status from the server: one and only one instance MUST be sent from the server
unless the call was cancelled - in which case this can be skipped */
GRPC_OP_SEND_STATUS_FROM_SERVER,
/* Receive initial metadata: one and only one MUST be made on the client, must
not be made on the server */
GRPC_OP_RECV_INITIAL_METADATA,
/* Receive a message: 0 or more of these operations can occur for each call */
GRPC_OP_RECV_MESSAGE,
/* Receive status on the client: one and only one must be made on the client */
GRPC_OP_RECV_STATUS_ON_CLIENT,
/* Receive status on the server: one and only one must be made on the server */
GRPC_OP_RECV_CLOSE_ON_SERVER
} grpc_op_type;
/* Operation data: one field for each op type (except SEND_CLOSE_FROM_CLIENT which has
no arguments) */
typedef struct grpc_op {
grpc_op_type op;
union {
struct {
size_t count;
const grpc_metadata *metadata;
} send_initial_metadata;
grpc_byte_buffer *send_message;
struct {
size_t trailing_metadata_count;
grpc_metadata *trailing_metadata;
grpc_status_code status;
const char *status_details;
} send_status_from_server;
/* ownership of the array is with the caller, but ownership of the elements
stays with the call object (ie key, value members are owned by the call
object, recv_initial_metadata->array is owned by the caller).
After the operation completes, call grpc_metadata_array_destroy on this
value, or reuse it in a future op. */
grpc_metadata_array *recv_initial_metadata;
grpc_byte_buffer **recv_message;
struct {
/* ownership of the array is with the caller, but ownership of the elements
stays with the call object (ie key, value members are owned by the call
object, trailing_metadata->array is owned by the caller).
After the operation completes, call grpc_metadata_array_destroy on this
value, or reuse it in a future op. */
grpc_metadata_array *trailing_metadata;
grpc_status_code *status;
/* status_details is a buffer owned by the application before the op completes
and after the op has completed. During the operation status_details may be
reallocated to a size larger than *status_details_capacity, in which case
*status_details_capacity will be updated with the new array capacity.
Pre-allocating space:
size_t my_capacity = 8;
char *my_details = gpr_malloc(my_capacity);
x.status_details = &my_details;
x.status_details_capacity = &my_capacity;
Not pre-allocating space:
size_t my_capacity = 0;
char *my_details = NULL;
x.status_details = &my_details;
x.status_details_capacity = &my_capacity;
After the call:
gpr_free(my_details); */
char **status_details;
size_t *status_details_capacity;
} recv_status_on_client;
struct {
/* out argument, set to 1 if the call failed in any way (seen as a cancellation
on the server), or 0 if the call succeeded */
int *cancelled;
} recv_close_on_server;
} data;
} grpc_op;
/* Initialize the grpc library */
void grpc_init(void);
/* Shutdown the grpc library */
/* Shut down the grpc library */
void grpc_shutdown(void);
grpc_completion_queue *grpc_completion_queue_create(void);
/* Blocks until an event is available, the completion queue is being shutdown,
/* Blocks until an event is available, the completion queue is being shut down,
or deadline is reached. Returns NULL on timeout, otherwise the event that
occurred. Callers should call grpc_event_finish once they have processed
the event.
@ -261,7 +366,7 @@ grpc_event *grpc_completion_queue_next(grpc_completion_queue *cq,
grpc_event *grpc_completion_queue_pluck(grpc_completion_queue *cq, void *tag,
gpr_timespec deadline);
/* Cleanup any data owned by the event */
/* Clean up any data owned by the event */
void grpc_event_finish(grpc_event *event);
/* Begin destruction of a completion queue. Once all possible events are
@ -275,8 +380,25 @@ void grpc_completion_queue_destroy(grpc_completion_queue *cq);
/* Create a call given a grpc_channel, in order to call 'method'. The request
is not sent until grpc_call_invoke is called. All completions are sent to
'completion_queue'. */
grpc_call *grpc_channel_create_call(grpc_channel *channel, const char *method,
const char *host, gpr_timespec deadline);
grpc_call *grpc_channel_create_call_old(grpc_channel *channel,
const char *method, const char *host,
gpr_timespec deadline);
/* Create a call given a grpc_channel, in order to call 'method'. The request
is not sent until grpc_call_invoke is called. All completions are sent to
'completion_queue'. */
grpc_call *grpc_channel_create_call(grpc_channel *channel,
grpc_completion_queue *completion_queue,
const char *method, const char *host,
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.
Only one operation of each type can be active at once in any given
batch. */
grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops,
size_t nops, void *tag);
/* Create a client channel */
grpc_channel *grpc_channel_create(const char *target,
@ -307,8 +429,9 @@ void grpc_channel_destroy(grpc_channel *channel);
REQUIRES: grpc_call_start_invoke/grpc_call_server_end_initial_metadata have
not been called on this call.
Produces no events. */
grpc_call_error grpc_call_add_metadata(grpc_call *call, grpc_metadata *metadata,
gpr_uint32 flags);
grpc_call_error grpc_call_add_metadata_old(grpc_call *call,
grpc_metadata *metadata,
gpr_uint32 flags);
/* Invoke the RPC. Starts sending metadata and request headers on the wire.
flags is a bit-field combination of the write flags defined above.
@ -319,9 +442,9 @@ grpc_call_error grpc_call_add_metadata(grpc_call *call, grpc_metadata *metadata,
Produces a GRPC_FINISHED event with finished_tag when the call has been
completed (there may be other events for the call pending at this
time) */
grpc_call_error grpc_call_invoke(grpc_call *call, grpc_completion_queue *cq,
void *metadata_read_tag, void *finished_tag,
gpr_uint32 flags);
grpc_call_error grpc_call_invoke_old(grpc_call *call, grpc_completion_queue *cq,
void *metadata_read_tag,
void *finished_tag, gpr_uint32 flags);
/* Accept an incoming RPC, binding a completion queue to it.
To be called before sending or receiving messages.
@ -330,9 +453,9 @@ grpc_call_error grpc_call_invoke(grpc_call *call, grpc_completion_queue *cq,
Produces a GRPC_FINISHED event with finished_tag when the call has been
completed (there may be other events for the call pending at this
time) */
grpc_call_error grpc_call_server_accept(grpc_call *call,
grpc_completion_queue *cq,
void *finished_tag);
grpc_call_error grpc_call_server_accept_old(grpc_call *call,
grpc_completion_queue *cq,
void *finished_tag);
/* Start sending metadata.
To be called before sending messages.
@ -340,8 +463,8 @@ grpc_call_error grpc_call_server_accept(grpc_call *call,
REQUIRES: Can be called at most once per call.
Can only be called on the server.
Must be called after grpc_call_server_accept */
grpc_call_error grpc_call_server_end_initial_metadata(grpc_call *call,
gpr_uint32 flags);
grpc_call_error grpc_call_server_end_initial_metadata_old(grpc_call *call,
gpr_uint32 flags);
/* Called by clients to cancel an RPC on the server.
Can be called multiple times, from any thread. */
@ -370,9 +493,9 @@ grpc_call_error grpc_call_cancel_with_status(grpc_call *call,
grpc_call_server_end_of_initial_metadata must have been called
successfully.
Produces a GRPC_WRITE_ACCEPTED event. */
grpc_call_error grpc_call_start_write(grpc_call *call,
grpc_byte_buffer *byte_buffer, void *tag,
gpr_uint32 flags);
grpc_call_error grpc_call_start_write_old(grpc_call *call,
grpc_byte_buffer *byte_buffer,
void *tag, gpr_uint32 flags);
/* Queue a status for writing.
REQUIRES: No other writes are pending on the call.
@ -380,17 +503,17 @@ grpc_call_error grpc_call_start_write(grpc_call *call,
call prior to calling this.
Only callable on the server.
Produces a GRPC_FINISH_ACCEPTED event when the status is sent. */
grpc_call_error grpc_call_start_write_status(grpc_call *call,
grpc_status_code status_code,
const char *status_message,
void *tag);
grpc_call_error grpc_call_start_write_status_old(grpc_call *call,
grpc_status_code status_code,
const char *status_message,
void *tag);
/* No more messages to send.
REQUIRES: No other writes are pending on the call.
Only callable on the client.
Produces a GRPC_FINISH_ACCEPTED event when all bytes for the call have passed
outgoing flow control. */
grpc_call_error grpc_call_writes_done(grpc_call *call, void *tag);
grpc_call_error grpc_call_writes_done_old(grpc_call *call, void *tag);
/* Initiate a read on a call. Output event contains a byte buffer with the
result of the read.
@ -402,7 +525,7 @@ grpc_call_error grpc_call_writes_done(grpc_call *call, void *tag);
On the server:
grpc_call_server_accept must be called before calling this.
Produces a single GRPC_READ event. */
grpc_call_error grpc_call_start_read(grpc_call *call, void *tag);
grpc_call_error grpc_call_start_read_old(grpc_call *call, void *tag);
/* Destroy a call. */
void grpc_call_destroy(grpc_call *call);
@ -414,7 +537,13 @@ void grpc_call_destroy(grpc_call *call);
tag_cancel.
REQUIRES: Server must not have been shutdown.
NOTE: calling this is the only way to obtain GRPC_SERVER_RPC_NEW events. */
grpc_call_error grpc_server_request_call(grpc_server *server, void *tag_new);
grpc_call_error grpc_server_request_call_old(grpc_server *server,
void *tag_new);
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 *completion_queue, void *tag_new);
/* Create a server */
grpc_server *grpc_server_create(grpc_completion_queue *cq,

@ -54,6 +54,12 @@ void grpc_credentials_release(grpc_credentials *creds);
/* Creates default credentials. */
grpc_credentials *grpc_default_credentials_create(void);
/* Environment variable that points to the default SSL roots file. This file
must be a PEM encoded file with all the roots such as the one that can be
downloaded from https://pki.google.com/roots.pem. */
#define GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR \
"GRPC_DEFAULT_SSL_ROOTS_FILE_PATH"
/* Object that holds a private key / certificate chain pair in PEM format. */
typedef struct {
/* private_key is the NULL-terminated string containing the PEM encoding of

@ -0,0 +1,53 @@
/*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef __GRPC_SUPPORT_LOG_WIN32_H__
#define __GRPC_SUPPORT_LOG_WIN32_H__
#include <windows.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Returns a string allocated with gpr_malloc that contains a UTF-8
* formatted error message, corresponding to the error messageid.
* Use in conjunction with GetLastError() et al.
*/
char *gpr_format_message(DWORD messageid);
#ifdef __cplusplus
}
#endif
#endif /* __GRPC_SUPPORT_LOG_H__ */

@ -37,39 +37,73 @@
/* Override this file with one for your platform if you need to redefine
things. */
/* For a common case, assume that the platform has a C99-like stdint.h */
#include <stdint.h>
#if !defined(GPR_NO_AUTODETECT_PLATFORM)
#if defined(_WIN64) || defined(WIN64)
#define GPR_WIN32 1
#define GPR_ARCH_64 1
#define GPR_GETPID_IN_PROCESS_H 1
#define GPR_WINSOCK_SOCKET 1
#elif defined(_WIN32) || defined(WIN32)
#define GPR_ARCH_32 1
#define GPR_WIN32 1
#define GPR_GETPID_IN_PROCESS_H 1
#define GPR_WINSOCK_SOCKET 1
#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_POSIX_MULTIPOLL_WITH_POLL 1
#define GPR_POSIX_WAKEUP_FD 1
#define GPR_LINUX_EVENTFD 1
#define GPR_POSIX_SOCKET 1
#define GPR_POSIX_SOCKETADDR 1
#define GPR_POSIX_SOCKETUTILS 1
#define GPR_POSIX_ENV 1
#define GPR_POSIX_FILE 1
#define GPR_POSIX_STRING 1
#define GPR_POSIX_SYNC 1
#define GPR_POSIX_TIME 1
#define GPR_GETPID_IN_UNISTD_H 1
#elif defined(__linux__)
#ifndef _BSD_SOURCE
#define _BSD_SOURCE
#endif
#ifndef _DEFAULT_SOURCE
#define _DEFAULT_SOURCE
#endif
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <features.h>
#define GPR_CPU_LINUX 1
#define GPR_GCC_ATOMIC 1
#define GPR_LINUX 1
#define GPR_POSIX_MULTIPOLL_WITH_POLL 1
#define GPR_LINUX_MULTIPOLL_WITH_EPOLL 1
#define GPR_POSIX_WAKEUP_FD 1
#define GPR_POSIX_SOCKET 1
#define GPR_POSIX_SOCKETADDR 1
#ifdef __GLIBC_PREREQ
#if __GLIBC_PREREQ(2, 9)
#define GPR_LINUX_EVENTFD 1
#endif
#if __GLIBC_PREREQ(2, 10)
#define GPR_LINUX_SOCKETUTILS 1
#endif
#if __GLIBC_PREREQ(2, 17)
#define GPR_LINUX_ENV 1
#endif
#endif
#ifndef GPR_LINUX_EVENTFD
#define GPR_POSIX_NO_SPECIAL_WAKEUP_FD 1
#endif
#ifndef GPR_LINUX_SOCKETUTILS
#define GPR_POSIX_SOCKETUTILS
#endif
#ifndef GPR_LINUX_ENV
#define GPR_POSIX_ENV 1
#endif
#define GPR_POSIX_FILE 1
#define GPR_POSIX_STRING 1
#define GPR_POSIX_SYNC 1
#define GPR_POSIX_TIME 1
@ -80,13 +114,20 @@
#define GPR_ARCH_32 1
#endif /* _LP64 */
#elif defined(__APPLE__)
#ifndef _BSD_SOURCE
#define _BSD_SOURCE
#endif
#define GPR_CPU_POSIX 1
#define GPR_GCC_ATOMIC 1
#define GPR_POSIX_LOG 1
#define GPR_POSIX_MULTIPOLL_WITH_POLL 1
#define GPR_POSIX_WAKEUP_FD 1
#define GPR_POSIX_NO_SPECIAL_WAKEUP_FD 1
#define GPR_POSIX_SOCKET 1
#define GPR_POSIX_SOCKETADDR 1
#define GPR_POSIX_SOCKETUTILS 1
#define GPR_POSIX_ENV 1
#define GPR_POSIX_FILE 1
#define GPR_POSIX_STRING 1
#define GPR_POSIX_SYNC 1
#define GPR_POSIX_TIME 1
@ -101,6 +142,10 @@
#endif
#endif /* GPR_NO_AUTODETECT_PLATFORM */
/* For a common case, assume that the platform has a C99-like stdint.h */
#include <stdint.h>
/* Cache line alignment */
#ifndef GPR_CACHELINE_SIZE
#if defined(__i386__) || defined(__x86_64__)
@ -153,7 +198,7 @@ typedef uintmax_t gpr_uintmax;
typedef uintptr_t gpr_uintptr;
/* INT64_MAX is unavailable on some platforms. */
#define GPR_INT64_MAX (~(gpr_uint64)0 >> 1)
#define GPR_INT64_MAX (gpr_int64)(~(gpr_uint64)0 >> 1)
/* maximum alignment needed for any type on this platform, rounded up to a
power of two */

@ -73,7 +73,7 @@ size_t gpr_slice_buffer_add_indexed(gpr_slice_buffer *sb, gpr_slice slice);
void gpr_slice_buffer_addn(gpr_slice_buffer *sb, gpr_slice *slices, size_t n);
/* add a very small (less than 8 bytes) amount of data to the end of a slice
buffer: returns a pointer into which to add the data */
gpr_uint8 *gpr_slice_buffer_tiny_add(gpr_slice_buffer *sb, int len);
gpr_uint8 *gpr_slice_buffer_tiny_add(gpr_slice_buffer *sb, unsigned len);
/* clear a slice buffer, unref all elements */
void gpr_slice_buffer_reset_and_unref(gpr_slice_buffer *sb);

@ -44,18 +44,12 @@
#include <grpc/support/port_platform.h>
#if defined(GPR_POSIX_SYNC)
#include <grpc/support/thd_posix.h>
#elif defined(GPR_WIN32)
#include <grpc/support/thd_win32.h>
#else
#error could not determine platform for thd
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef gpr_uint64 gpr_thd_id;
/* Thread creation options. */
typedef struct {
int flags; /* Flags below can be set here. Default value 0. */
@ -72,6 +66,9 @@ int gpr_thd_new(gpr_thd_id *t, void (*thd_body)(void *arg), void *arg,
/* Return a gpr_thd_options struct with all fields set to defaults. */
gpr_thd_options gpr_thd_options_default(void);
/* Returns the identifier of the current thread. */
gpr_thd_id gpr_thd_currentid(void);
#ifdef __cplusplus
}
#endif

@ -34,31 +34,22 @@
#ifndef __GRPC_SUPPORT_TIME_H__
#define __GRPC_SUPPORT_TIME_H__
/* Time support.
We use gpr_timespec, which is typedefed to struct timespec on platforms which
have it. On some machines, absolute times may be in local time. */
/* Platform specific header declares gpr_timespec.
gpr_timespec contains:
time_t tv_sec; // seconds since start of 1970
int tv_nsec; // nanoseconds; always in 0..999999999; never negative.
*/
We use gpr_timespec, which is analogous to struct timespec. On some
machines, absolute times may be in local time. */
#include <grpc/support/port_platform.h>
#if defined(GPR_POSIX_TIME)
#include <grpc/support/time_posix.h>
#elif defined(GPR_WIN32)
#include <grpc/support/time_win32.h>
#else
#error could not determine platform for time
#endif
#include <stddef.h>
#include <time.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct gpr_timespec {
time_t tv_sec;
int tv_nsec;
} gpr_timespec;
/* Time constants. */
extern const gpr_timespec gpr_time_0; /* The zero time interval. */
extern const gpr_timespec gpr_inf_future; /* The far future */
@ -103,10 +94,6 @@ int gpr_time_similar(gpr_timespec a, gpr_timespec b, gpr_timespec threshold);
/* Sleep until at least 'until' - an absolute timeout */
void gpr_sleep_until(gpr_timespec until);
struct timeval gpr_timeval_from_timespec(gpr_timespec t);
gpr_timespec gpr_timespec_from_timeval(struct timeval t);
double gpr_timespec_to_micros(gpr_timespec t);
#ifdef __cplusplus

@ -105,7 +105,7 @@ void grpc_channel_args_destroy(grpc_channel_args *a) {
}
int grpc_channel_args_is_census_enabled(const grpc_channel_args *a) {
int i;
unsigned i;
if (a == NULL) return 0;
for (i = 0; i < a->num_args; i++) {
if (0 == strcmp(a->args[i].key, GRPC_ARG_ENABLE_CENSUS)) {

@ -125,7 +125,8 @@ void grpc_channel_stack_init(const grpc_channel_filter **filters,
call_size += ROUND_UP_TO_ALIGNMENT_SIZE(filters[i]->sizeof_call_data);
}
GPR_ASSERT(user_data - (char *)stack ==
GPR_ASSERT(user_data > (char *)stack);
GPR_ASSERT((gpr_uintptr)(user_data - (char *)stack) ==
grpc_channel_stack_size(filters, filter_count));
stack->call_stack_size = call_size;
@ -209,6 +210,7 @@ void grpc_call_element_recv_metadata(grpc_call_element *cur_elem,
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);
}
@ -220,6 +222,7 @@ void grpc_call_element_send_metadata(grpc_call_element *cur_elem,
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);
}
@ -230,14 +233,16 @@ void grpc_call_element_send_cancel(grpc_call_element *cur_elem) {
cancel_op.dir = GRPC_CALL_DOWN;
cancel_op.done_cb = do_nothing;
cancel_op.user_data = NULL;
cancel_op.flags = 0;
grpc_call_next_op(cur_elem, &cancel_op);
}
void grpc_call_element_send_finish(grpc_call_element *cur_elem) {
grpc_call_op cancel_op;
cancel_op.type = GRPC_SEND_FINISH;
cancel_op.dir = GRPC_CALL_DOWN;
cancel_op.done_cb = do_nothing;
cancel_op.user_data = NULL;
grpc_call_next_op(cur_elem, &cancel_op);
grpc_call_op finish_op;
finish_op.type = GRPC_SEND_FINISH;
finish_op.dir = GRPC_CALL_DOWN;
finish_op.done_cb = do_nothing;
finish_op.user_data = NULL;
finish_op.flags = 0;
grpc_call_next_op(cur_elem, &finish_op);
}

@ -210,11 +210,30 @@ static void remove_waiting_child(channel_data *chand, call_data *calld) {
chand->waiting_child_count = new_count;
}
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);
/* send up a finish */
finish_op.type = GRPC_RECV_FINISH;
finish_op.dir = GRPC_CALL_UP;
finish_op.flags = 0;
finish_op.done_cb = do_nothing;
finish_op.user_data = NULL;
grpc_call_next_op(elem, &finish_op);
}
static void cancel_rpc(grpc_call_element *elem, grpc_call_op *op) {
call_data *calld = elem->call_data;
channel_data *chand = elem->channel_data;
grpc_call_element *child_elem;
grpc_call_op finish_op;
gpr_mu_lock(&chand->mu);
switch (calld->state) {
@ -225,27 +244,16 @@ static void cancel_rpc(grpc_call_element *elem, grpc_call_op *op) {
return; /* early out */
case CALL_WAITING:
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);
/* fallthrough intended */
return; /* early out */
case CALL_CREATED:
calld->state = CALL_CANCELLED;
gpr_mu_unlock(&chand->mu);
/* 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);
/* send up a finish */
finish_op.type = GRPC_RECV_FINISH;
finish_op.dir = GRPC_CALL_UP;
finish_op.flags = 0;
finish_op.done_cb = do_nothing;
finish_op.user_data = NULL;
grpc_call_next_op(elem, &finish_op);
send_up_cancelled_ops(elem);
return; /* early out */
case CALL_CANCELLED:
gpr_mu_unlock(&chand->mu);
@ -298,6 +306,7 @@ static void channel_op(grpc_channel_element *elem,
grpc_channel_element *from_elem, grpc_channel_op *op) {
channel_data *chand = elem->channel_data;
grpc_child_channel *child_channel;
grpc_channel_op rop;
GPR_ASSERT(elem->filter == &grpc_client_channel_filter);
switch (op->type) {
@ -323,6 +332,10 @@ static void channel_op(grpc_channel_element *elem,
if (child_channel) {
grpc_child_channel_destroy(child_channel, 1);
}
/* fake a transport closed to satisfy the refcounting in client */
rop.type = GRPC_TRANSPORT_CLOSED;
rop.dir = GRPC_CALL_UP;
grpc_channel_next_op(elem, &rop);
break;
case GRPC_TRANSPORT_GOAWAY:
/* receiving goaway: if it's from our active child, drop the active child;

@ -298,10 +298,6 @@ static void recv_error(channel_data *chand, call_data *calld, int line,
static void do_nothing(void *calldata, grpc_op_error error) {}
static void done_message(void *user_data, grpc_op_error error) {
grpc_byte_buffer_destroy(user_data);
}
static void finish_message(channel_data *chand, call_data *calld) {
grpc_call_element *elem = calld->elem;
grpc_call_op call_op;
@ -309,9 +305,9 @@ static void finish_message(channel_data *chand, call_data *calld) {
call_op.flags = 0;
/* if we got all the bytes for this message, call up the stack */
call_op.type = GRPC_RECV_MESSAGE;
call_op.done_cb = done_message;
call_op.done_cb = do_nothing;
/* TODO(ctiller): this could be a lot faster if coded directly */
call_op.user_data = call_op.data.message = grpc_byte_buffer_create(
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);
@ -471,17 +467,11 @@ static void transport_goaway(void *user_data, grpc_transport *transport,
/* transport got goaway ==> call up and handle it */
grpc_channel_element *elem = user_data;
channel_data *chand = elem->channel_data;
char *msg;
grpc_channel_op op;
GPR_ASSERT(elem->filter == &grpc_connected_channel_filter);
GPR_ASSERT(chand->transport == transport);
msg = gpr_hexdump((const char *)GPR_SLICE_START_PTR(debug),
GPR_SLICE_LENGTH(debug), GPR_HEXDUMP_PLAINTEXT);
gpr_log(GPR_DEBUG, "got goaway: status=%d, message=%s", status, msg);
gpr_free(msg);
op.type = GRPC_TRANSPORT_GOAWAY;
op.dir = GRPC_CALL_UP;
op.data.goaway.status = status;

@ -1,5 +1,4 @@
/*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
@ -44,6 +43,7 @@ typedef struct channel_data {
grpc_mdelem *method;
grpc_mdelem *scheme;
grpc_mdelem *content_type;
grpc_mdelem *status;
} channel_data;
/* used to silence 'variable not used' warnings */
@ -86,6 +86,18 @@ static void call_op(grpc_call_element *elem, grpc_call_element *from_elem,
grpc_call_element_send_metadata(elem, 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);
}
break;
default:
/* pass control up or down the stack depending on op->dir */
grpc_call_next_op(elem, op);
@ -134,7 +146,7 @@ static void destroy_call_elem(grpc_call_element *elem) {
}
static const char *scheme_from_args(const grpc_channel_args *args) {
int i;
unsigned i;
if (args != NULL) {
for (i = 0; i < args->num_args; ++i) {
if (args->args[i].type == GRPC_ARG_STRING &&
@ -166,6 +178,7 @@ static void init_channel_elem(grpc_channel_element *elem,
grpc_mdelem_from_strings(mdctx, ":scheme", scheme_from_args(args));
channeld->content_type =
grpc_mdelem_from_strings(mdctx, "content-type", "application/grpc");
channeld->status = grpc_mdelem_from_strings(mdctx, ":status", "200");
}
/* Destructor for channel data */
@ -177,6 +190,7 @@ static void destroy_channel_elem(grpc_channel_element *elem) {
grpc_mdelem_unref(channeld->method);
grpc_mdelem_unref(channeld->scheme);
grpc_mdelem_unref(channeld->content_type);
grpc_mdelem_unref(channeld->status);
}
const grpc_channel_filter grpc_http_client_filter = {

@ -319,8 +319,8 @@ static void init_channel_elem(grpc_channel_element *elem,
if (channeld->gettable_count == gettable_capacity) {
gettable_capacity =
GPR_MAX(gettable_capacity * 3 / 2, gettable_capacity + 1);
channeld->gettables =
gpr_realloc(channeld->gettables, gettable_capacity * sizeof(gettable));
channeld->gettables = gpr_realloc(channeld->gettables,
gettable_capacity * sizeof(gettable));
}
g = &channeld->gettables[channeld->gettable_count++];
g->path = grpc_mdelem_from_strings(mdctx, ":path", p->path);
@ -328,15 +328,25 @@ static void init_channel_elem(grpc_channel_element *elem,
grpc_mdelem_from_strings(mdctx, "content-type", p->content_type);
slice = gpr_slice_from_copied_string(p->content);
g->content = grpc_byte_buffer_create(&slice, 1);
gpr_slice_unref(slice);
}
}
}
/* Destructor for channel data */
static void destroy_channel_elem(grpc_channel_element *elem) {
size_t i;
/* grab pointers to our data from the channel element */
channel_data *channeld = elem->channel_data;
for (i = 0; i < channeld->gettable_count; i++) {
grpc_mdelem_unref(channeld->gettables[i].path);
grpc_mdelem_unref(channeld->gettables[i].content_type);
grpc_byte_buffer_destroy(channeld->gettables[i].content);
}
gpr_free(channeld->gettables);
grpc_mdelem_unref(channeld->te_trailers);
grpc_mdelem_unref(channeld->status_ok);
grpc_mdelem_unref(channeld->status_not_found);
@ -350,6 +360,6 @@ static void destroy_channel_elem(grpc_channel_element *elem) {
}
const grpc_channel_filter grpc_http_server_filter = {
call_op, channel_op, sizeof(call_data),
init_call_elem, destroy_call_elem, sizeof(channel_data),
init_channel_elem, destroy_channel_elem, "http-server"};
call_op, channel_op, sizeof(call_data), init_call_elem, destroy_call_elem,
sizeof(channel_data), init_channel_elem, destroy_channel_elem,
"http-server"};

@ -105,6 +105,8 @@ gpr_slice grpc_httpcli_format_post_request(const grpc_httpcli_request *request,
}
gpr_strvec_add(&out, gpr_strdup("\r\n"));
tmp = gpr_strvec_flatten(&out, &out_len);
gpr_strvec_destroy(&out);
if (body_bytes) {
tmp = gpr_realloc(tmp, out_len + body_size);
memcpy(tmp + out_len, body_bytes, body_size);

@ -73,20 +73,23 @@ static grpc_security_status httpcli_ssl_create_handshaker(
return GRPC_SECURITY_OK;
}
static grpc_security_status httpcli_ssl_check_peer(
grpc_security_context *ctx, const tsi_peer *peer,
grpc_security_check_peer_cb cb, void *user_data) {
static grpc_security_status httpcli_ssl_check_peer(grpc_security_context *ctx,
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_security_status status = GRPC_SECURITY_OK;
/* Check the peer name. */
if (c->secure_peer_name != NULL &&
!tsi_ssl_peer_matches_name(peer, c->secure_peer_name)) {
!tsi_ssl_peer_matches_name(&peer, c->secure_peer_name)) {
gpr_log(GPR_ERROR, "Peer name %s is not in peer certificate",
c->secure_peer_name);
return GRPC_SECURITY_ERROR;
status = GRPC_SECURITY_ERROR;
}
return GRPC_SECURITY_OK;
tsi_peer_destruct(&peer);
return status;
}
static grpc_security_context_vtable httpcli_ssl_vtable = {

@ -335,10 +335,6 @@ static int run_some_expired_alarms(gpr_mu *drop_mu, gpr_timespec now,
gpr_mu_unlock(&g_mu);
gpr_mu_unlock(&g_checker_mu);
} else if (next) {
gpr_mu_lock(&g_mu);
*next = gpr_time_min(*next, g_shard_queue[0]->min_deadline);
gpr_mu_unlock(&g_mu);
}
if (n && drop_mu) {

@ -39,6 +39,15 @@
/* iomgr internal api for dealing with alarms */
/* Check for alarms to be run, and run them.
Return non zero if alarm callbacks were executed.
Drops drop_mu if it is non-null before executing callbacks.
If next is non-null, TRY to update *next with the next running alarm
IF that alarm occurs before *next current value.
*next is never guaranteed to be updated on any given execution; however,
with high probability at least one thread in the system will see an update
at any time slice. */
int grpc_alarm_check(gpr_mu *drop_mu, gpr_timespec now, gpr_timespec *next);
void grpc_alarm_list_init(gpr_timespec now);

@ -47,12 +47,60 @@
enum descriptor_state { NOT_READY, READY, WAITING };
/* We need to keep a freelist not because of any concerns of malloc performance
* but instead so that implementations with multiple threads in (for example)
* epoll_wait deal with the race between pollset removal and incoming poll
* notifications.
*
* The problem is that the poller ultimately holds a reference to this
* object, so it is very difficult to know when is safe to free it, at least
* without some expensive synchronization.
*
* If we keep the object freelisted, in the worst case losing this race just
* becomes a spurious read notification on a reused fd.
*/
/* TODO(klempner): We could use some form of polling generation count to know
* when these are safe to free. */
/* TODO(klempner): Consider disabling freelisting if we don't have multiple
* threads in poll on the same fd */
/* TODO(klempner): Batch these allocations to reduce fragmentation */
static grpc_fd *fd_freelist = NULL;
static gpr_mu fd_freelist_mu;
static void freelist_fd(grpc_fd *fd) {
gpr_mu_lock(&fd_freelist_mu);
fd->freelist_next = fd_freelist;
fd_freelist = fd;
gpr_mu_unlock(&fd_freelist_mu);
}
static grpc_fd *alloc_fd(int fd) {
grpc_fd *r = NULL;
gpr_mu_lock(&fd_freelist_mu);
if (fd_freelist != NULL) {
r = fd_freelist;
fd_freelist = fd_freelist->freelist_next;
}
gpr_mu_unlock(&fd_freelist_mu);
if (r == NULL) {
r = gpr_malloc(sizeof(grpc_fd));
gpr_mu_init(&r->set_state_mu);
gpr_mu_init(&r->watcher_mu);
}
gpr_atm_rel_store(&r->refst, 1);
gpr_atm_rel_store(&r->readst.state, NOT_READY);
gpr_atm_rel_store(&r->writest.state, NOT_READY);
gpr_atm_rel_store(&r->shutdown, 0);
r->fd = fd;
r->watcher_root.next = r->watcher_root.prev = &r->watcher_root;
r->freelist_next = NULL;
return r;
}
static void destroy(grpc_fd *fd) {
grpc_iomgr_add_callback(fd->on_done, fd->on_done_user_data);
gpr_mu_destroy(&fd->set_state_mu);
gpr_free(fd->watchers);
gpr_mu_destroy(&fd->watcher_mu);
gpr_free(fd);
grpc_iomgr_unref();
}
static void ref_by(grpc_fd *fd, int n) {
@ -61,25 +109,28 @@ static void ref_by(grpc_fd *fd, int n) {
static void unref_by(grpc_fd *fd, int n) {
if (gpr_atm_full_fetch_add(&fd->refst, -n) == n) {
grpc_iomgr_add_callback(fd->on_done, fd->on_done_user_data);
freelist_fd(fd);
grpc_iomgr_unref();
}
}
void grpc_fd_global_init(void) { gpr_mu_init(&fd_freelist_mu); }
void grpc_fd_global_shutdown(void) {
while (fd_freelist != NULL) {
grpc_fd *fd = fd_freelist;
fd_freelist = fd_freelist->freelist_next;
destroy(fd);
}
gpr_mu_destroy(&fd_freelist_mu);
}
static void do_nothing(void *ignored, int success) {}
grpc_fd *grpc_fd_create(int fd) {
grpc_fd *r = gpr_malloc(sizeof(grpc_fd));
grpc_fd *r = alloc_fd(fd);
grpc_iomgr_ref();
gpr_atm_rel_store(&r->refst, 1);
gpr_atm_rel_store(&r->readst.state, NOT_READY);
gpr_atm_rel_store(&r->writest.state, NOT_READY);
gpr_mu_init(&r->set_state_mu);
gpr_mu_init(&r->watcher_mu);
gpr_atm_rel_store(&r->shutdown, 0);
r->fd = fd;
r->watchers = NULL;
r->watcher_count = 0;
r->watcher_capacity = 0;
grpc_pollset_add_fd(grpc_backup_pollset(), r);
return r;
}
@ -89,11 +140,11 @@ int grpc_fd_is_orphaned(grpc_fd *fd) {
}
static void wake_watchers(grpc_fd *fd) {
size_t i, n;
grpc_fd_watcher *watcher;
gpr_mu_lock(&fd->watcher_mu);
n = fd->watcher_count;
for (i = 0; i < n; i++) {
grpc_pollset_force_kick(fd->watchers[i]);
for (watcher = fd->watcher_root.next; watcher != &fd->watcher_root;
watcher = watcher->next) {
grpc_pollset_force_kick(watcher->pollset);
}
gpr_mu_unlock(&fd->watcher_mu);
}
@ -237,36 +288,27 @@ void grpc_fd_notify_on_write(grpc_fd *fd, grpc_iomgr_cb_func write_cb,
}
gpr_uint32 grpc_fd_begin_poll(grpc_fd *fd, grpc_pollset *pollset,
gpr_uint32 read_mask, gpr_uint32 write_mask) {
gpr_uint32 read_mask, gpr_uint32 write_mask,
grpc_fd_watcher *watcher) {
/* keep track of pollers that have requested our events, in case they change
*/
gpr_mu_lock(&fd->watcher_mu);
if (fd->watcher_capacity == fd->watcher_count) {
fd->watcher_capacity =
GPR_MAX(fd->watcher_capacity + 8, fd->watcher_capacity * 3 / 2);
fd->watchers = gpr_realloc(fd->watchers,
fd->watcher_capacity * sizeof(grpc_pollset *));
}
fd->watchers[fd->watcher_count++] = pollset;
watcher->next = &fd->watcher_root;
watcher->prev = watcher->next->prev;
watcher->next->prev = watcher->prev->next = watcher;
watcher->pollset = pollset;
watcher->fd = fd;
gpr_mu_unlock(&fd->watcher_mu);
return (gpr_atm_acq_load(&fd->readst.state) != READY ? read_mask : 0) |
(gpr_atm_acq_load(&fd->writest.state) != READY ? write_mask : 0);
}
void grpc_fd_end_poll(grpc_fd *fd, grpc_pollset *pollset) {
size_t r, w, n;
gpr_mu_lock(&fd->watcher_mu);
n = fd->watcher_count;
for (r = 0, w = 0; r < n; r++) {
if (fd->watchers[r] == pollset) {
fd->watcher_count--;
continue;
}
fd->watchers[w++] = fd->watchers[r];
}
gpr_mu_unlock(&fd->watcher_mu);
void grpc_fd_end_poll(grpc_fd_watcher *watcher) {
gpr_mu_lock(&watcher->fd->watcher_mu);
watcher->next->prev = watcher->prev;
watcher->prev->next = watcher->next;
gpr_mu_unlock(&watcher->fd->watcher_mu);
}
void grpc_fd_become_readable(grpc_fd *fd, int allow_synchronous_callback) {

@ -47,7 +47,16 @@ typedef struct {
gpr_atm state;
} grpc_fd_state;
typedef struct grpc_fd {
typedef struct grpc_fd grpc_fd;
typedef struct grpc_fd_watcher {
struct grpc_fd_watcher *next;
struct grpc_fd_watcher *prev;
grpc_pollset *pollset;
grpc_fd *fd;
} grpc_fd_watcher;
struct grpc_fd {
int fd;
/* refst format:
bit0: 1=active/0=orphaned
@ -60,16 +69,15 @@ typedef struct grpc_fd {
gpr_atm shutdown;
gpr_mu watcher_mu;
grpc_pollset **watchers;
size_t watcher_count;
size_t watcher_capacity;
grpc_fd_watcher watcher_root;
grpc_fd_state readst;
grpc_fd_state writest;
grpc_iomgr_cb_func on_done;
void *on_done_user_data;
} grpc_fd;
struct grpc_fd *freelist_next;
};
/* Create a wrapped file descriptor.
Requires fd is a non-blocking file descriptor.
@ -94,9 +102,10 @@ void grpc_fd_orphan(grpc_fd *fd, grpc_iomgr_cb_func on_done, void *user_data);
Polling strategies that do not need to alter their behavior depending on the
fd's current interest (such as epoll) do not need to call this function. */
gpr_uint32 grpc_fd_begin_poll(grpc_fd *fd, grpc_pollset *pollset,
gpr_uint32 read_mask, gpr_uint32 write_mask);
gpr_uint32 read_mask, gpr_uint32 write_mask,
grpc_fd_watcher *rec);
/* Complete polling previously started with grpc_fd_begin_poll */
void grpc_fd_end_poll(grpc_fd *fd, grpc_pollset *pollset);
void grpc_fd_end_poll(grpc_fd_watcher *rec);
/* Return 1 if this fd is orphaned, 0 otherwise */
int grpc_fd_is_orphaned(grpc_fd *fd);
@ -135,4 +144,7 @@ void grpc_fd_become_writable(grpc_fd *fd, int allow_synchronous_callback);
void grpc_fd_ref(grpc_fd *fd);
void grpc_fd_unref(grpc_fd *fd);
void grpc_fd_global_init(void);
void grpc_fd_global_shutdown(void);
#endif /* __GRPC_INTERNAL_IOMGR_FD_POSIX_H_ */

@ -0,0 +1,200 @@
/*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <grpc/support/port_platform.h>
#ifdef GPR_WINSOCK_SOCKET
#include <winsock2.h>
#include <grpc/support/log.h>
#include <grpc/support/log_win32.h>
#include <grpc/support/alloc.h>
#include <grpc/support/thd.h>
#include "src/core/iomgr/alarm_internal.h"
#include "src/core/iomgr/iocp_windows.h"
#include "src/core/iomgr/iomgr_internal.h"
#include "src/core/iomgr/socket_windows.h"
static ULONG g_iocp_kick_token;
static OVERLAPPED g_iocp_custom_overlap;
static gpr_event g_shutdown_iocp;
static gpr_event g_iocp_done;
static HANDLE g_iocp;
static int do_iocp_work() {
BOOL success;
DWORD bytes = 0;
DWORD flags = 0;
ULONG_PTR completion_key;
LPOVERLAPPED overlapped;
gpr_timespec wait_time = gpr_inf_future;
grpc_winsocket *socket;
grpc_winsocket_callback_info *info;
void(*f)(void *, int) = NULL;
void *opaque = NULL;
success = GetQueuedCompletionStatus(g_iocp, &bytes,
&completion_key, &overlapped,
gpr_time_to_millis(wait_time));
if (!success && !overlapped) {
/* The deadline got attained. */
return 0;
}
GPR_ASSERT(completion_key && overlapped);
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 1;
}
gpr_log(GPR_ERROR, "Unknown custom completion key.");
abort();
}
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");
abort();
}
success = WSAGetOverlappedResult(socket->socket, &info->overlapped, &bytes,
FALSE, &flags);
gpr_log(GPR_DEBUG, "bytes: %u, flags: %u - op %s", bytes, flags,
success ? "succeeded" : "failed");
info->bytes_transfered = bytes;
info->wsa_error = success ? 0 : WSAGetLastError();
GPR_ASSERT(overlapped == &info->overlapped);
gpr_mu_lock(&socket->state_mu);
GPR_ASSERT(!info->has_pending_iocp);
if (info->cb) {
f = info->cb;
opaque = info->opaque;
info->cb = NULL;
} else {
info->has_pending_iocp = 1;
}
gpr_mu_unlock(&socket->state_mu);
if (f) f(opaque, 1);
return 1;
}
static void iocp_loop(void *p) {
while (!gpr_event_get(&g_shutdown_iocp)) {
grpc_maybe_call_delayed_callbacks(NULL, 1);
do_iocp_work();
}
gpr_event_set(&g_iocp_done, (void *)1);
}
void grpc_iocp_init(void) {
gpr_thd_id id;
g_iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL,
(ULONG_PTR)NULL, 0);
GPR_ASSERT(g_iocp);
gpr_event_init(&g_iocp_done);
gpr_event_init(&g_shutdown_iocp);
gpr_thd_new(&id, iocp_loop, NULL, NULL);
}
void grpc_iocp_shutdown(void) {
BOOL success;
gpr_event_set(&g_shutdown_iocp, (void *)1);
success = PostQueuedCompletionStatus(g_iocp, 0,
(ULONG_PTR) &g_iocp_kick_token,
&g_iocp_custom_overlap);
GPR_ASSERT(success);
gpr_event_wait(&g_iocp_done, gpr_inf_future);
success = CloseHandle(g_iocp);
GPR_ASSERT(success);
}
void grpc_iocp_add_socket(grpc_winsocket *socket) {
HANDLE ret;
if (socket->added_to_iocp) return;
ret = CreateIoCompletionPort((HANDLE)socket->socket,
g_iocp, (gpr_uintptr) socket, 0);
if (!ret) {
char *utf8_message = gpr_format_message(WSAGetLastError());
gpr_log(GPR_ERROR, "Unable to add socket to iocp: %s", utf8_message);
gpr_free(utf8_message);
__debugbreak();
abort();
}
socket->added_to_iocp = 1;
GPR_ASSERT(ret == g_iocp);
}
static void socket_notify_on_iocp(grpc_winsocket *socket,
void(*cb)(void *, int), void *opaque,
grpc_winsocket_callback_info *info) {
int run_now = 0;
GPR_ASSERT(!info->cb);
gpr_mu_lock(&socket->state_mu);
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);
}
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);
}
#endif /* GPR_WINSOCK_SOCKET */

@ -0,0 +1,52 @@
/*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE 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_IOMGR_IOCP_WINDOWS_H_
#define __GRPC_INTERNAL_IOMGR_IOCP_WINDOWS_H_
#include <windows.h>
#include <grpc/support/sync.h>
#include "src/core/iomgr/socket_windows.h"
void grpc_iocp_init(void);
void grpc_iocp_shutdown(void);
void grpc_iocp_add_socket(grpc_winsocket *);
void grpc_socket_notify_on_write(grpc_winsocket *, void(*cb)(void *, int success),
void *opaque);
void grpc_socket_notify_on_read(grpc_winsocket *, void(*cb)(void *, int success),
void *opaque);
#endif /* __GRPC_INTERNAL_IOMGR_IOCP_WINDOWS_H_ */

@ -51,6 +51,7 @@ typedef struct delayed_callback {
static gpr_mu g_mu;
static gpr_cv g_cv;
static gpr_cv g_rcv;
static delayed_callback *g_cbs_head = NULL;
static delayed_callback *g_cbs_tail = NULL;
static int g_shutdown;
@ -86,6 +87,7 @@ void grpc_iomgr_init(void) {
gpr_thd_id id;
gpr_mu_init(&g_mu);
gpr_cv_init(&g_cv);
gpr_cv_init(&g_rcv);
grpc_alarm_list_init(gpr_now());
g_refs = 0;
grpc_iomgr_platform_init();
@ -98,7 +100,6 @@ void grpc_iomgr_shutdown(void) {
gpr_timespec shutdown_deadline =
gpr_time_add(gpr_now(), gpr_time_from_seconds(10));
grpc_iomgr_platform_shutdown();
gpr_mu_lock(&g_mu);
g_shutdown = 1;
@ -116,7 +117,7 @@ void grpc_iomgr_shutdown(void) {
gpr_mu_lock(&g_mu);
}
if (g_refs) {
if (gpr_cv_wait(&g_cv, &g_mu, shutdown_deadline) && g_cbs_head == NULL) {
if (gpr_cv_wait(&g_rcv, &g_mu, shutdown_deadline) && g_cbs_head == NULL) {
gpr_log(GPR_DEBUG,
"Failed to free %d iomgr objects before shutdown deadline: "
"memory leaks are likely",
@ -127,11 +128,14 @@ void grpc_iomgr_shutdown(void) {
}
gpr_mu_unlock(&g_mu);
grpc_kick_poller();
gpr_event_wait(&g_background_callback_executor_done, gpr_inf_future);
grpc_iomgr_platform_shutdown();
grpc_alarm_list_shutdown();
gpr_mu_destroy(&g_mu);
gpr_cv_destroy(&g_cv);
gpr_cv_destroy(&g_rcv);
}
void grpc_iomgr_ref(void) {
@ -143,7 +147,7 @@ void grpc_iomgr_ref(void) {
void grpc_iomgr_unref(void) {
gpr_mu_lock(&g_mu);
if (0 == --g_refs) {
gpr_cv_signal(&g_cv);
gpr_cv_signal(&g_rcv);
}
gpr_mu_unlock(&g_mu);
}

@ -31,8 +31,21 @@
*
*/
#include <grpc/support/port_platform.h>
#ifdef GPR_POSIX_SOCKET
#include "src/core/iomgr/iomgr_posix.h"
#include "src/core/iomgr/fd_posix.h"
void grpc_iomgr_platform_init(void) {
grpc_fd_global_init();
grpc_pollset_global_init();
}
void grpc_iomgr_platform_init(void) { grpc_pollset_global_init(); }
void grpc_iomgr_platform_shutdown(void) {
grpc_pollset_global_shutdown();
grpc_fd_global_shutdown();
}
void grpc_iomgr_platform_shutdown(void) { grpc_pollset_global_shutdown(); }
#endif /* GRPC_POSIX_SOCKET */

@ -0,0 +1,67 @@
/*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING 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_win32.h"
#include <grpc/support/log.h>
#include "src/core/iomgr/socket_windows.h"
#include "src/core/iomgr/iocp_windows.h"
#include "src/core/iomgr/iomgr.h"
static void winsock_init(void) {
WSADATA wsaData;
int status = WSAStartup(MAKEWORD(2, 0), &wsaData);
GPR_ASSERT(status == 0);
}
static void winsock_shutdown(void) {
int status = WSACleanup();
GPR_ASSERT(status == 0);
}
void grpc_iomgr_platform_init(void) {
winsock_init();
grpc_iocp_init();
}
void grpc_iomgr_platform_shutdown(void) {
grpc_iocp_shutdown();
winsock_shutdown();
}
#endif /* GRPC_WINSOCK_SOCKET */

@ -34,98 +34,74 @@
#include <grpc/support/port_platform.h>
#ifdef GPR_POSIX_SOCKET
#include "src/core/iomgr/pollset_kick_posix.h"
#include "src/core/iomgr/pollset_kick.h"
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include "src/core/iomgr/socket_utils_posix.h"
#include "src/core/iomgr/wakeup_fd_posix.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
/* This implementation is based on a freelist of pipes. */
#define GRPC_MAX_CACHED_PIPES 50
#define GRPC_PIPE_LOW_WATERMARK 25
/* This implementation is based on a freelist of wakeup fds, with extra logic to
* handle kicks while there is no attached fd. */
typedef struct grpc_kick_pipe_info {
int pipe_read_fd;
int pipe_write_fd;
struct grpc_kick_pipe_info *next;
} grpc_kick_pipe_info;
/* TODO(klempner): Autosize this, and consider providing a way to disable the
* cap entirely on systems with large fd limits */
#define GRPC_MAX_CACHED_WFDS 50
static grpc_kick_pipe_info *pipe_freelist = NULL;
static int pipe_freelist_count = 0;
static gpr_mu pipe_freelist_mu;
static grpc_kick_fd_info *fd_freelist = NULL;
static int fd_freelist_count = 0;
static gpr_mu fd_freelist_mu;
static grpc_kick_pipe_info *allocate_pipe(void) {
grpc_kick_pipe_info *info;
gpr_mu_lock(&pipe_freelist_mu);
if (pipe_freelist != NULL) {
info = pipe_freelist;
pipe_freelist = pipe_freelist->next;
--pipe_freelist_count;
} else {
int pipefd[2];
/* TODO(klempner): Make this nonfatal */
GPR_ASSERT(0 == pipe(pipefd));
GPR_ASSERT(grpc_set_socket_nonblocking(pipefd[0], 1));
GPR_ASSERT(grpc_set_socket_nonblocking(pipefd[1], 1));
static grpc_kick_fd_info *allocate_wfd(void) {
grpc_kick_fd_info *info = NULL;
gpr_mu_lock(&fd_freelist_mu);
if (fd_freelist != NULL) {
info = fd_freelist;
fd_freelist = fd_freelist->next;
--fd_freelist_count;
}
gpr_mu_unlock(&fd_freelist_mu);
if (info == NULL) {
info = gpr_malloc(sizeof(*info));
info->pipe_read_fd = pipefd[0];
info->pipe_write_fd = pipefd[1];
grpc_wakeup_fd_create(&info->wakeup_fd);
info->next = NULL;
}
gpr_mu_unlock(&pipe_freelist_mu);
return info;
}
static void destroy_pipe(void) {
/* assumes pipe_freelist_mu is held */
grpc_kick_pipe_info *current = pipe_freelist;
pipe_freelist = pipe_freelist->next;
pipe_freelist_count--;
close(current->pipe_read_fd);
close(current->pipe_write_fd);
gpr_free(current);
static void destroy_wfd(grpc_kick_fd_info* wfd) {
grpc_wakeup_fd_destroy(&wfd->wakeup_fd);
gpr_free(wfd);
}
static void free_pipe(grpc_kick_pipe_info *pipe_info) {
gpr_mu_lock(&pipe_freelist_mu);
pipe_info->next = pipe_freelist;
pipe_freelist = pipe_info;
pipe_freelist_count++;
if (pipe_freelist_count > GRPC_MAX_CACHED_PIPES) {
while (pipe_freelist_count > GRPC_PIPE_LOW_WATERMARK) {
destroy_pipe();
}
static void free_wfd(grpc_kick_fd_info *fd_info) {
gpr_mu_lock(&fd_freelist_mu);
if (fd_freelist_count < GRPC_MAX_CACHED_WFDS) {
fd_info->next = fd_freelist;
fd_freelist = fd_info;
fd_freelist_count++;
fd_info = NULL;
}
gpr_mu_unlock(&pipe_freelist_mu);
}
void grpc_pollset_kick_global_init() {
pipe_freelist = NULL;
gpr_mu_init(&pipe_freelist_mu);
}
gpr_mu_unlock(&fd_freelist_mu);
void grpc_pollset_kick_global_destroy() {
while (pipe_freelist != NULL) {
destroy_pipe();
if (fd_info) {
destroy_wfd(fd_info);
}
gpr_mu_destroy(&pipe_freelist_mu);
}
void grpc_pollset_kick_init(grpc_pollset_kick_state *kick_state) {
gpr_mu_init(&kick_state->mu);
kick_state->kicked = 0;
kick_state->pipe_info = NULL;
kick_state->fd_info = NULL;
}
void grpc_pollset_kick_destroy(grpc_pollset_kick_state *kick_state) {
gpr_mu_destroy(&kick_state->mu);
GPR_ASSERT(kick_state->pipe_info == NULL);
GPR_ASSERT(kick_state->fd_info == NULL);
}
int grpc_pollset_kick_pre_poll(grpc_pollset_kick_state *kick_state) {
@ -135,49 +111,51 @@ int grpc_pollset_kick_pre_poll(grpc_pollset_kick_state *kick_state) {
gpr_mu_unlock(&kick_state->mu);
return -1;
}
kick_state->pipe_info = allocate_pipe();
kick_state->fd_info = allocate_wfd();
gpr_mu_unlock(&kick_state->mu);
return kick_state->pipe_info->pipe_read_fd;
return GRPC_WAKEUP_FD_GET_READ_FD(&kick_state->fd_info->wakeup_fd);
}
void grpc_pollset_kick_consume(grpc_pollset_kick_state *kick_state) {
char buf[128];
int r;
for (;;) {
r = read(kick_state->pipe_info->pipe_read_fd, buf, sizeof(buf));
if (r > 0) continue;
if (r == 0) return;
switch (errno) {
case EAGAIN:
return;
case EINTR:
continue;
default:
gpr_log(GPR_ERROR, "error reading pipe: %s", strerror(errno));
return;
}
}
grpc_wakeup_fd_consume_wakeup(&kick_state->fd_info->wakeup_fd);
}
void grpc_pollset_kick_post_poll(grpc_pollset_kick_state *kick_state) {
gpr_mu_lock(&kick_state->mu);
free_pipe(kick_state->pipe_info);
kick_state->pipe_info = NULL;
free_wfd(kick_state->fd_info);
kick_state->fd_info = NULL;
gpr_mu_unlock(&kick_state->mu);
}
void grpc_pollset_kick_kick(grpc_pollset_kick_state *kick_state) {
gpr_mu_lock(&kick_state->mu);
if (kick_state->pipe_info != NULL) {
char c = 0;
while (write(kick_state->pipe_info->pipe_write_fd, &c, 1) != 1 &&
errno == EINTR)
;
if (kick_state->fd_info != NULL) {
grpc_wakeup_fd_wakeup(&kick_state->fd_info->wakeup_fd);
} else {
kick_state->kicked = 1;
}
gpr_mu_unlock(&kick_state->mu);
}
#endif
void grpc_pollset_kick_global_init_fallback_fd(void) {
gpr_mu_init(&fd_freelist_mu);
grpc_wakeup_fd_global_init_force_fallback();
}
void grpc_pollset_kick_global_init(void) {
gpr_mu_init(&fd_freelist_mu);
grpc_wakeup_fd_global_init();
}
void grpc_pollset_kick_global_destroy(void) {
while (fd_freelist != NULL) {
grpc_kick_fd_info *current = fd_freelist;
fd_freelist = fd_freelist->next;
destroy_wfd(current);
}
grpc_wakeup_fd_global_destroy();
gpr_mu_destroy(&fd_freelist_mu);
}
#endif /* GPR_POSIX_SOCKET */

@ -36,9 +36,6 @@
#include <grpc/support/port_platform.h>
/* This is an abstraction around the typical pipe mechanism for waking up a
thread sitting in a poll() style call. */
#ifdef GPR_POSIX_SOCKET
#include "src/core/iomgr/pollset_kick_posix.h"
#endif
@ -47,12 +44,19 @@
#include "src/core/iomgr/pollset_kick_windows.h"
#endif
/* This is an abstraction around the typical pipe mechanism for waking up a
thread sitting in a poll() style call. */
void grpc_pollset_kick_global_init(void);
void grpc_pollset_kick_global_destroy(void);
void grpc_pollset_kick_init(grpc_pollset_kick_state *kick_state);
void grpc_pollset_kick_destroy(grpc_pollset_kick_state *kick_state);
/* Guarantees a pure posix implementation rather than a specialized one, if
* applicable. Intended for testing. */
void grpc_pollset_kick_global_init_fallback_fd(void);
/* Must be called before entering poll(). If return value is -1, this consumed
an existing kick. Otherwise the return value is an FD to add to the poll set.
*/

@ -34,14 +34,18 @@
#ifndef __GRPC_INTERNAL_IOMGR_POLLSET_KICK_POSIX_H_
#define __GRPC_INTERNAL_IOMGR_POLLSET_KICK_POSIX_H_
#include "src/core/iomgr/wakeup_fd_posix.h"
#include <grpc/support/sync.h>
struct grpc_kick_pipe_info;
typedef struct grpc_kick_fd_info {
grpc_wakeup_fd_info wakeup_fd;
struct grpc_kick_fd_info *next;
} grpc_kick_fd_info;
typedef struct grpc_pollset_kick_state {
gpr_mu mu;
int kicked;
struct grpc_kick_pipe_info *pipe_info;
struct grpc_kick_fd_info *fd_info;
} grpc_pollset_kick_state;
#endif /* __GRPC_INTERNAL_IOMGR_POLLSET_KICK_POSIX_H_ */
#endif /* __GRPC_INTERNALIOMGR_POLLSET_KICK_POSIX_H_ */

@ -36,10 +36,10 @@
#include <grpc/support/sync.h>
struct grpc_kick_pipe_info;
struct grpc_kick_fd_info;
typedef struct grpc_pollset_kick_state {
int unused;
} grpc_pollset_kick_state;
#endif /* __GRPC_INTERNAL_IOMGR_POLLSET_KICK_WINDOWS_H_ */
#endif /* __GRPC_INTERNALIOMGR_POLLSET_KICK_WINDOWS_H_ */

@ -0,0 +1,197 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <grpc/support/port_platform.h>
#ifdef GPR_LINUX_MULTIPOLL_WITH_EPOLL
#include <errno.h>
#include <string.h>
#include <sys/epoll.h>
#include <unistd.h>
#include "src/core/iomgr/fd_posix.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
typedef struct {
int epoll_fd;
grpc_wakeup_fd_info wakeup_fd;
} pollset_hdr;
static void multipoll_with_epoll_pollset_add_fd(grpc_pollset *pollset,
grpc_fd *fd) {
pollset_hdr *h = pollset->data.ptr;
struct epoll_event ev;
int err;
ev.events = EPOLLIN | EPOLLOUT | EPOLLET;
ev.data.ptr = fd;
err = epoll_ctl(h->epoll_fd, EPOLL_CTL_ADD, fd->fd, &ev);
if (err < 0) {
/* FDs may be added to a pollset multiple times, so EEXIST is normal. */
if (errno != EEXIST) {
gpr_log(GPR_ERROR, "epoll_ctl add for %d failed: %s", fd->fd,
strerror(errno));
}
}
}
static void multipoll_with_epoll_pollset_del_fd(grpc_pollset *pollset,
grpc_fd *fd) {
pollset_hdr *h = pollset->data.ptr;
int err;
/* Note that this can race with concurrent poll, but that should be fine since
* at worst it creates a spurious read event on a reused grpc_fd object. */
err = epoll_ctl(h->epoll_fd, EPOLL_CTL_DEL, fd->fd, NULL);
if (err < 0) {
gpr_log(GPR_ERROR, "epoll_ctl del for %d failed: %s", fd->fd,
strerror(errno));
}
}
/* TODO(klempner): We probably want to turn this down a bit */
#define GRPC_EPOLL_MAX_EVENTS 1000
static int multipoll_with_epoll_pollset_maybe_work(
grpc_pollset *pollset, gpr_timespec deadline, gpr_timespec now,
int allow_synchronous_callback) {
struct epoll_event ep_ev[GRPC_EPOLL_MAX_EVENTS];
int ep_rv;
pollset_hdr *h = pollset->data.ptr;
int timeout_ms;
/* If you want to ignore epoll's ability to sanely handle parallel pollers,
* for a more apples-to-apples performance comparison with poll, add a
* if (pollset->counter == 0) { return 0 }
* here.
*/
if (gpr_time_cmp(deadline, gpr_inf_future) == 0) {
timeout_ms = -1;
} else {
timeout_ms = gpr_time_to_millis(gpr_time_sub(deadline, now));
if (timeout_ms <= 0) {
return 1;
}
}
pollset->counter += 1;
gpr_mu_unlock(&pollset->mu);
do {
ep_rv = epoll_wait(h->epoll_fd, ep_ev, GRPC_EPOLL_MAX_EVENTS, timeout_ms);
if (ep_rv < 0) {
if (errno != EINTR) {
gpr_log(GPR_ERROR, "epoll_wait() failed: %s", strerror(errno));
}
} else {
int i;
for (i = 0; i < ep_rv; ++i) {
if (ep_ev[i].data.ptr == 0) {
grpc_wakeup_fd_consume_wakeup(&h->wakeup_fd);
} else {
grpc_fd *fd = ep_ev[i].data.ptr;
/* TODO(klempner): We might want to consider making err and pri
* separate events */
int cancel = ep_ev[i].events & (EPOLLERR | EPOLLHUP);
int read = ep_ev[i].events & (EPOLLIN | EPOLLPRI);
int write = ep_ev[i].events & EPOLLOUT;
if (read || cancel) {
grpc_fd_become_readable(fd, allow_synchronous_callback);
}
if (write || cancel) {
grpc_fd_become_writable(fd, allow_synchronous_callback);
}
}
}
}
timeout_ms = 0;
} while (ep_rv == GRPC_EPOLL_MAX_EVENTS);
gpr_mu_lock(&pollset->mu);
pollset->counter -= 1;
/* TODO(klempner): This should signal once per event rather than broadcast,
* although it probably doesn't matter because threads will generally be
* blocked in epoll_wait rather than being blocked on the cv. */
gpr_cv_broadcast(&pollset->cv);
return 1;
}
static void multipoll_with_epoll_pollset_destroy(grpc_pollset *pollset) {
pollset_hdr *h = pollset->data.ptr;
grpc_wakeup_fd_destroy(&h->wakeup_fd);
close(h->epoll_fd);
gpr_free(h);
}
static void epoll_kick(grpc_pollset *pollset) {
pollset_hdr *h = pollset->data.ptr;
grpc_wakeup_fd_wakeup(&h->wakeup_fd);
}
static const grpc_pollset_vtable multipoll_with_epoll_pollset = {
multipoll_with_epoll_pollset_add_fd, multipoll_with_epoll_pollset_del_fd,
multipoll_with_epoll_pollset_maybe_work, epoll_kick,
multipoll_with_epoll_pollset_destroy};
void grpc_platform_become_multipoller(grpc_pollset *pollset, grpc_fd **fds,
size_t nfds) {
size_t i;
pollset_hdr *h = gpr_malloc(sizeof(pollset_hdr));
struct epoll_event ev;
int err;
pollset->vtable = &multipoll_with_epoll_pollset;
pollset->data.ptr = h;
h->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (h->epoll_fd < 0) {
/* TODO(klempner): Fall back to poll here, especially on ENOSYS */
gpr_log(GPR_ERROR, "epoll_create1 failed: %s", strerror(errno));
abort();
}
for (i = 0; i < nfds; i++) {
multipoll_with_epoll_pollset_add_fd(pollset, fds[i]);
}
grpc_wakeup_fd_create(&h->wakeup_fd);
ev.events = EPOLLIN;
ev.data.ptr = 0;
err = epoll_ctl(h->epoll_fd, EPOLL_CTL_ADD,
GRPC_WAKEUP_FD_GET_READ_FD(&h->wakeup_fd), &ev);
if (err < 0) {
gpr_log(GPR_ERROR, "Wakeup fd epoll_ctl failed: %s", strerror(errno));
abort();
}
}
#endif /* GPR_LINUX_MULTIPOLL_WITH_EPOLL */

@ -53,11 +53,11 @@ typedef struct {
size_t fd_count;
size_t fd_capacity;
grpc_fd **fds;
/* fds being polled by the current poller: parallel arrays of pollfd and the
* grpc_fd* that the pollfd was constructed from */
/* fds being polled by the current poller: parallel arrays of pollfd, and
a grpc_fd_watcher */
size_t pfd_count;
size_t pfd_capacity;
grpc_fd **selfds;
grpc_fd_watcher *watchers;
struct pollfd *pfds;
/* fds that have been removed from the pollset explicitly */
size_t del_count;
@ -98,7 +98,7 @@ static void end_polling(grpc_pollset *pollset) {
pollset_hdr *h;
h = pollset->data.ptr;
for (i = 1; i < h->pfd_count; i++) {
grpc_fd_end_poll(h->selfds[i], pollset);
grpc_fd_end_poll(&h->watchers[i]);
}
}
@ -125,9 +125,9 @@ static int multipoll_with_poll_pollset_maybe_work(
if (h->pfd_capacity < h->fd_count + 1) {
h->pfd_capacity = GPR_MAX(h->pfd_capacity * 3 / 2, h->fd_count + 1);
gpr_free(h->pfds);
gpr_free(h->selfds);
gpr_free(h->watchers);
h->pfds = gpr_malloc(sizeof(struct pollfd) * h->pfd_capacity);
h->selfds = gpr_malloc(sizeof(grpc_fd *) * h->pfd_capacity);
h->watchers = gpr_malloc(sizeof(grpc_fd_watcher) * h->pfd_capacity);
}
nf = 0;
np = 1;
@ -147,9 +147,7 @@ static int multipoll_with_poll_pollset_maybe_work(
grpc_fd_unref(h->fds[i]);
} else {
h->fds[nf++] = h->fds[i];
h->pfds[np].events =
grpc_fd_begin_poll(h->fds[i], pollset, POLLIN, POLLOUT);
h->selfds[np] = h->fds[i];
h->watchers[np].fd = h->fds[i];
h->pfds[np].fd = h->fds[i]->fd;
h->pfds[np].revents = 0;
np++;
@ -168,6 +166,11 @@ static int multipoll_with_poll_pollset_maybe_work(
pollset->counter = 1;
gpr_mu_unlock(&pollset->mu);
for (i = 1; i < np; i++) {
h->pfds[i].events = grpc_fd_begin_poll(h->watchers[i].fd, pollset, POLLIN,
POLLOUT, &h->watchers[i]);
}
r = poll(h->pfds, h->pfd_count, timeout);
if (r < 0) {
if (errno != EINTR) {
@ -181,10 +184,10 @@ static int multipoll_with_poll_pollset_maybe_work(
}
for (i = 1; i < np; i++) {
if (h->pfds[i].revents & POLLIN) {
grpc_fd_become_readable(h->selfds[i], allow_synchronous_callback);
grpc_fd_become_readable(h->watchers[i].fd, allow_synchronous_callback);
}
if (h->pfds[i].revents & POLLOUT) {
grpc_fd_become_writable(h->selfds[i], allow_synchronous_callback);
grpc_fd_become_writable(h->watchers[i].fd, allow_synchronous_callback);
}
}
}
@ -197,6 +200,10 @@ static int multipoll_with_poll_pollset_maybe_work(
return 1;
}
static void multipoll_with_poll_pollset_kick(grpc_pollset *p) {
grpc_pollset_kick_kick(&p->kick_state);
}
static void multipoll_with_poll_pollset_destroy(grpc_pollset *pollset) {
size_t i;
pollset_hdr *h = pollset->data.ptr;
@ -208,7 +215,7 @@ static void multipoll_with_poll_pollset_destroy(grpc_pollset *pollset) {
grpc_fd_unref(h->dels[i]);
}
gpr_free(h->pfds);
gpr_free(h->selfds);
gpr_free(h->watchers);
gpr_free(h->fds);
gpr_free(h->dels);
gpr_free(h);
@ -216,7 +223,7 @@ static void multipoll_with_poll_pollset_destroy(grpc_pollset *pollset) {
static const grpc_pollset_vtable multipoll_with_poll_pollset = {
multipoll_with_poll_pollset_add_fd, multipoll_with_poll_pollset_del_fd,
multipoll_with_poll_pollset_maybe_work,
multipoll_with_poll_pollset_maybe_work, multipoll_with_poll_pollset_kick,
multipoll_with_poll_pollset_destroy};
void grpc_platform_become_multipoller(grpc_pollset *pollset, grpc_fd **fds,
@ -231,7 +238,7 @@ void grpc_platform_become_multipoller(grpc_pollset *pollset, grpc_fd **fds,
h->pfd_count = 0;
h->pfd_capacity = 0;
h->pfds = NULL;
h->selfds = NULL;
h->watchers = NULL;
h->del_count = 0;
h->del_capacity = 0;
h->dels = NULL;

@ -75,11 +75,18 @@ static void backup_poller(void *p) {
}
void grpc_pollset_kick(grpc_pollset *p) {
if (!p->counter) return;
if (p->counter) {
p->vtable->kick(p);
}
}
void grpc_pollset_force_kick(grpc_pollset *p) {
grpc_pollset_kick_kick(&p->kick_state);
}
void grpc_pollset_force_kick(grpc_pollset *p) { grpc_pollset_kick(p); }
static void kick_using_pollset_kick(grpc_pollset *p) {
grpc_pollset_kick_kick(&p->kick_state);
}
/* global state management */
@ -183,7 +190,7 @@ static void empty_pollset_destroy(grpc_pollset *pollset) {}
static const grpc_pollset_vtable empty_pollset = {
empty_pollset_add_fd, empty_pollset_del_fd, empty_pollset_maybe_work,
empty_pollset_destroy};
kick_using_pollset_kick, empty_pollset_destroy};
static void become_empty_pollset(grpc_pollset *pollset) {
pollset->vtable = &empty_pollset;
@ -199,8 +206,15 @@ static void unary_poll_pollset_add_fd(grpc_pollset *pollset, grpc_fd *fd) {
if (fd == pollset->data.ptr) return;
fds[0] = pollset->data.ptr;
fds[1] = fd;
grpc_platform_become_multipoller(pollset, fds, GPR_ARRAY_SIZE(fds));
grpc_fd_unref(fds[0]);
if (!grpc_fd_is_orphaned(fds[0])) {
grpc_platform_become_multipoller(pollset, fds, GPR_ARRAY_SIZE(fds));
grpc_fd_unref(fds[0]);
} else {
/* old fd is orphaned and we haven't cleaned it up until now, so remain a
* unary poller */
grpc_fd_unref(fds[0]);
pollset->data.ptr = fd;
}
}
static void unary_poll_pollset_del_fd(grpc_pollset *pollset, grpc_fd *fd) {
@ -216,6 +230,7 @@ static int unary_poll_pollset_maybe_work(grpc_pollset *pollset,
int allow_synchronous_callback) {
struct pollfd pfd[2];
grpc_fd *fd;
grpc_fd_watcher fd_watcher;
int timeout;
int r;
@ -244,11 +259,12 @@ static int unary_poll_pollset_maybe_work(grpc_pollset *pollset,
pfd[0].events = POLLIN;
pfd[0].revents = 0;
pfd[1].fd = fd->fd;
pfd[1].events = grpc_fd_begin_poll(fd, pollset, POLLIN, POLLOUT);
pfd[1].revents = 0;
pollset->counter = 1;
gpr_mu_unlock(&pollset->mu);
pfd[1].events = grpc_fd_begin_poll(fd, pollset, POLLIN, POLLOUT, &fd_watcher);
r = poll(pfd, GPR_ARRAY_SIZE(pfd), timeout);
if (r < 0) {
if (errno != EINTR) {
@ -269,9 +285,9 @@ static int unary_poll_pollset_maybe_work(grpc_pollset *pollset,
}
grpc_pollset_kick_post_poll(&pollset->kick_state);
grpc_fd_end_poll(&fd_watcher);
gpr_mu_lock(&pollset->mu);
grpc_fd_end_poll(fd, pollset);
pollset->counter = 0;
gpr_cv_broadcast(&pollset->cv);
return 1;
@ -284,7 +300,8 @@ static void unary_poll_pollset_destroy(grpc_pollset *pollset) {
static const grpc_pollset_vtable unary_poll_pollset = {
unary_poll_pollset_add_fd, unary_poll_pollset_del_fd,
unary_poll_pollset_maybe_work, unary_poll_pollset_destroy};
unary_poll_pollset_maybe_work, kick_using_pollset_kick,
unary_poll_pollset_destroy};
static void become_unary_pollset(grpc_pollset *pollset, grpc_fd *fd) {
pollset->vtable = &unary_poll_pollset;

@ -66,6 +66,7 @@ struct grpc_pollset_vtable {
void (*del_fd)(grpc_pollset *pollset, struct grpc_fd *fd);
int (*maybe_work)(grpc_pollset *pollset, gpr_timespec deadline,
gpr_timespec now, int allow_synchronous_callback);
void (*kick)(grpc_pollset *pollset);
void (*destroy)(grpc_pollset *pollset);
};
@ -78,7 +79,11 @@ void grpc_pollset_add_fd(grpc_pollset *pollset, struct grpc_fd *fd);
poll after an fd is orphaned) */
void grpc_pollset_del_fd(grpc_pollset *pollset, struct grpc_fd *fd);
/* Force any current pollers to break polling */
/* Force any current pollers to break polling: it's the callers responsibility
to ensure that the pollset indeed needs to be kicked - no verification that
the pollset is actually performing polling work is done. At worst this will
result in spurious wakeups if performed at the wrong moment.
Does not touch pollset->mu. */
void grpc_pollset_force_kick(grpc_pollset *pollset);
/* Returns the fd to listen on for kicks */
int grpc_kick_read_fd(grpc_pollset *p);

@ -33,6 +33,39 @@
#include <grpc/support/port_platform.h>
#ifdef GPR_WIN32
#ifdef GPR_WINSOCK_SOCKET
#endif /* GPR_WIN32 */
#include <grpc/support/thd.h>
#include "src/core/iomgr/alarm_internal.h"
#include "src/core/iomgr/iomgr_internal.h"
#include "src/core/iomgr/pollset_windows.h"
void grpc_pollset_init(grpc_pollset *pollset) {
gpr_mu_init(&pollset->mu);
gpr_cv_init(&pollset->cv);
}
void grpc_pollset_destroy(grpc_pollset *pollset) {
gpr_mu_destroy(&pollset->mu);
gpr_cv_destroy(&pollset->cv);
}
int grpc_pollset_work(grpc_pollset *pollset, gpr_timespec deadline) {
gpr_timespec now;
now = gpr_now();
if (gpr_time_cmp(now, deadline) > 0) {
return 0;
}
if (grpc_maybe_call_delayed_callbacks(NULL, 1)) {
return 1;
}
if (grpc_alarm_check(NULL, now, &deadline)) {
return 1;
}
return 0;
}
void grpc_pollset_kick(grpc_pollset *p) { }
#endif /* GPR_WINSOCK_SOCKET */

@ -34,9 +34,11 @@
#ifndef __GRPC_INTERNAL_IOMGR_POLLSET_WINDOWS_H_
#define __GRPC_INTERNAL_IOMGR_POLLSET_WINDOWS_H_
#include <windows.h>
#include <grpc/support/sync.h>
#include "src/core/iomgr/pollset_kick.h"
#include "src/core/iomgr/socket_windows.h"
/* forward declare only in this file to avoid leaking impl details via
pollset.h; real users of grpc_fd should always include 'fd_posix.h' and not

@ -31,12 +31,15 @@
*
*/
#ifndef _POSIX_SOURCE
#define _POSIX_SOURCE
#endif
#include "src/core/iomgr/sockaddr.h"
#include "src/core/iomgr/resolve_address.h"
#include <sys/types.h>
#include <sys/un.h>
#include <string.h>
#include "src/core/iomgr/iomgr_internal.h"
@ -121,6 +124,19 @@ grpc_resolved_addresses *grpc_blocking_resolve_address(
size_t i;
grpc_resolved_addresses *addrs = NULL;
const gpr_timespec start_time = gpr_now();
struct sockaddr_un *un;
if (name[0] == 'u' && name[1] == 'n' && name[2] == 'i' && name[3] == 'x' &&
name[4] == ':' && name[5] != 0) {
addrs = gpr_malloc(sizeof(grpc_resolved_addresses));
addrs->naddrs = 1;
addrs->addrs = gpr_malloc(sizeof(grpc_resolved_address));
un = (struct sockaddr_un *)addrs->addrs->addr;
un->sun_family = AF_UNIX;
strcpy(un->sun_path, name + 5);
addrs->addrs->len = strlen(un->sun_path) + sizeof(un->sun_family);
return addrs;
}
/* parse name, splitting it into host and port parts */
split_host_port(name, &host, &port);

@ -111,13 +111,20 @@ int grpc_sockaddr_is_wildcard(const struct sockaddr *addr, int *port_out) {
void grpc_sockaddr_make_wildcards(int port, struct sockaddr_in *wild4_out,
struct sockaddr_in6 *wild6_out) {
memset(wild4_out, 0, sizeof(*wild4_out));
wild4_out->sin_family = AF_INET;
wild4_out->sin_port = htons(port);
grpc_sockaddr_make_wildcard4(port, wild4_out);
grpc_sockaddr_make_wildcard6(port, wild6_out);
}
void grpc_sockaddr_make_wildcard4(int port, struct sockaddr_in *wild_out) {
memset(wild_out, 0, sizeof(*wild_out));
wild_out->sin_family = AF_INET;
wild_out->sin_port = htons(port);
}
memset(wild6_out, 0, sizeof(*wild6_out));
wild6_out->sin6_family = AF_INET6;
wild6_out->sin6_port = htons(port);
void grpc_sockaddr_make_wildcard6(int port, struct sockaddr_in6 *wild_out) {
memset(wild_out, 0, sizeof(*wild_out));
wild_out->sin6_family = AF_INET6;
wild_out->sin6_port = htons(port);
}
int grpc_sockaddr_to_string(char **out, const struct sockaddr *addr,
@ -159,6 +166,8 @@ int grpc_sockaddr_get_port(const struct sockaddr *addr) {
return ntohs(((struct sockaddr_in *)addr)->sin_port);
case AF_INET6:
return ntohs(((struct sockaddr_in6 *)addr)->sin6_port);
case AF_UNIX:
return 1;
default:
gpr_log(GPR_ERROR, "Unknown socket family %d in %s", addr->sa_family,
__FUNCTION__);

@ -57,6 +57,12 @@ int grpc_sockaddr_is_wildcard(const struct sockaddr *addr, int *port_out);
void grpc_sockaddr_make_wildcards(int port, struct sockaddr_in *wild4_out,
struct sockaddr_in6 *wild6_out);
/* Writes 0.0.0.0:port. */
void grpc_sockaddr_make_wildcard4(int port, struct sockaddr_in *wild_out);
/* Writes [::]:port. */
void grpc_sockaddr_make_wildcard6(int port, struct sockaddr_in6 *wild_out);
/* Return the IP port number of a sockaddr */
int grpc_sockaddr_get_port(const struct sockaddr *addr);

@ -35,5 +35,7 @@
#define __GRPC_INTERNAL_IOMGR_SOCKADDR_WIN32_H_
#include <ws2tcpip.h>
#include <winsock2.h>
#include <mswsock.h>
#endif // __GRPC_INTERNAL_IOMGR_SOCKADDR_WIN32_H_
#endif /* __GRPC_INTERNAL_IOMGR_SOCKADDR_WIN32_H_ */

@ -31,10 +31,9 @@
*
*/
#define _GNU_SOURCE
#include <grpc/support/port_platform.h>
#ifdef GPR_LINUX
#ifdef GPR_LINUX_SOCKETUTILS
#include "src/core/iomgr/socket_utils_posix.h"

@ -35,7 +35,6 @@
#ifdef GPR_POSIX_SOCKETUTILS
#define _BSD_SOURCE
#include "src/core/iomgr/socket_utils_posix.h"
#include <fcntl.h>

@ -0,0 +1,77 @@
/*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <grpc/support/port_platform.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#ifdef GPR_WINSOCK_SOCKET
#include "src/core/iomgr/iocp_windows.h"
#include "src/core/iomgr/iomgr.h"
#include "src/core/iomgr/iomgr_internal.h"
#include "src/core/iomgr/socket_windows.h"
#include "src/core/iomgr/pollset.h"
#include "src/core/iomgr/pollset_windows.h"
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);
grpc_iomgr_ref();
grpc_iocp_add_socket(r);
return r;
}
void shutdown_op(grpc_winsocket_callback_info *info) {
if (!info->cb) return;
grpc_iomgr_add_delayed_callback(info->cb, info->opaque, 0);
}
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_iomgr_unref();
closesocket(socket->socket);
gpr_mu_destroy(&socket->state_mu);
gpr_free(socket);
}
#endif /* GPR_WINSOCK_SOCKET */

@ -31,42 +31,45 @@
*
*/
#include <grpc/grpc.h>
#include <grpc/support/log.h>
#include <google/gflags.h>
#include <grpc++/channel_interface.h>
#include <grpc++/create_channel.h>
#include <grpc++/status.h>
#ifndef __GRPC_INTERNAL_IOMGR_HANDLE_WINDOWS_H__
#define __GRPC_INTERNAL_IOMGR_HANDLE_WINDOWS_H__
#include "examples/tips/client.h"
#include "test/cpp/util/create_test_channel.h"
#include <windows.h>
DEFINE_int32(server_port, 443, "Server port.");
DEFINE_string(server_host,
"pubsub-staging.googleapis.com", "Server host to connect to");
#include <grpc/support/sync.h>
#include <grpc/support/atm.h>
int main(int argc, char** argv) {
grpc_init();
google::ParseCommandLineFlags(&argc, &argv, true);
gpr_log(GPR_INFO, "Start TIPS client");
typedef struct grpc_winsocket_callback_info {
/* This is supposed to be a WSAOVERLAPPED, but in order to get that
* definition, we need to include ws2tcpip.h, which needs to be included
* from the top, otherwise it'll clash with a previous inclusion of
* windows.h that in turns includes winsock.h. If anyone knows a way
* to do it properly, feel free to send a patch.
*/
OVERLAPPED overlapped;
void(*cb)(void *opaque, int success);
void *opaque;
int has_pending_iocp;
DWORD bytes_transfered;
int wsa_error;
} grpc_winsocket_callback_info;
const int host_port_buf_size = 1024;
char host_port[host_port_buf_size];
snprintf(host_port, host_port_buf_size, "%s:%d", FLAGS_server_host.c_str(),
FLAGS_server_port);
typedef struct grpc_winsocket {
SOCKET socket;
std::shared_ptr<grpc::ChannelInterface> channel(
grpc::CreateTestChannel(host_port,
FLAGS_server_host,
true, // enable SSL
true)); // use prod roots
int added_to_iocp;
grpc::examples::tips::Client client(channel);
grpc::Status s = client.CreateTopic("test");
gpr_log(GPR_INFO, "return code %d", s.code());
GPR_ASSERT(s.IsOk());
grpc_winsocket_callback_info write_info;
grpc_winsocket_callback_info read_info;
channel.reset();
grpc_shutdown();
return 0;
}
gpr_mu state_mu;
} grpc_winsocket;
/* Create a wrapped windows handle.
This takes ownership of closing it. */
grpc_winsocket *grpc_winsocket_create(SOCKET socket);
void grpc_winsocket_shutdown(grpc_winsocket *socket);
void grpc_winsocket_orphan(grpc_winsocket *socket);
#endif /* __GRPC_INTERNAL_IOMGR_HANDLE_WINDOWS_H__ */

@ -62,13 +62,13 @@ typedef struct {
int refs;
} async_connect;
static int prepare_socket(int fd) {
static int prepare_socket(const struct sockaddr *addr, int fd) {
if (fd < 0) {
goto error;
}
if (!grpc_set_socket_nonblocking(fd, 1) || !grpc_set_socket_cloexec(fd, 1) ||
!grpc_set_socket_low_latency(fd, 1)) {
(addr->sa_family != AF_UNIX && !grpc_set_socket_low_latency(fd, 1))) {
gpr_log(GPR_ERROR, "Unable to configure socket %d: %s", fd,
strerror(errno));
goto error;
@ -200,7 +200,7 @@ void grpc_tcp_client_connect(void (*cb)(void *arg, grpc_endpoint *ep),
addr = (struct sockaddr *)&addr4_copy;
addr_len = sizeof(addr4_copy);
}
if (!prepare_socket(fd)) {
if (!prepare_socket(addr, fd)) {
cb(arg, NULL);
return;
}

@ -0,0 +1,215 @@
/*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING 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_win32.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/log_win32.h>
#include <grpc/support/slice_buffer.h>
#include <grpc/support/useful.h>
#include "src/core/iomgr/tcp_client.h"
#include "src/core/iomgr/tcp_windows.h"
#include "src/core/iomgr/socket_windows.h"
#include "src/core/iomgr/alarm.h"
#include "src/core/iomgr/sockaddr.h"
#include "src/core/iomgr/sockaddr_utils.h"
typedef struct {
void(*cb)(void *arg, grpc_endpoint *tcp);
void *cb_arg;
gpr_mu mu;
grpc_winsocket *socket;
gpr_timespec deadline;
grpc_alarm alarm;
int refs;
} async_connect;
static void async_connect_cleanup(async_connect *ac) {
int done = (--ac->refs == 0);
gpr_mu_unlock(&ac->mu);
if (done) {
gpr_mu_destroy(&ac->mu);
gpr_free(ac);
}
}
static void on_alarm(void *acp, int success) {
async_connect *ac = acp;
gpr_mu_lock(&ac->mu);
if (ac->socket != NULL && success) {
grpc_winsocket_shutdown(ac->socket);
}
async_connect_cleanup(ac);
}
static void on_connect(void *acp, int success) {
async_connect *ac = acp;
SOCKET sock = ac->socket->socket;
grpc_endpoint *ep = NULL;
grpc_winsocket_callback_info *info = &ac->socket->write_info;
void(*cb)(void *arg, grpc_endpoint *tcp) = ac->cb;
void *cb_arg = ac->cb_arg;
grpc_alarm_cancel(&ac->alarm);
if (success) {
DWORD transfered_bytes = 0;
DWORD flags;
BOOL wsa_success = WSAGetOverlappedResult(sock, &info->overlapped,
&transfered_bytes, FALSE,
&flags);
GPR_ASSERT(transfered_bytes == 0);
if (!wsa_success) {
char *utf8_message = gpr_format_message(WSAGetLastError());
gpr_log(GPR_ERROR, "on_connect error: %s", utf8_message);
gpr_free(utf8_message);
goto finish;
} else {
gpr_log(GPR_DEBUG, "on_connect: connection established");
ep = grpc_tcp_create(ac->socket);
goto finish;
}
} else {
gpr_log(GPR_ERROR, "on_connect is shutting down");
goto finish;
}
abort();
finish:
gpr_mu_lock(&ac->mu);
if (!ep) {
grpc_winsocket_orphan(ac->socket);
}
async_connect_cleanup(ac);
cb(cb_arg, ep);
}
void grpc_tcp_client_connect(void(*cb)(void *arg, grpc_endpoint *tcp),
void *arg, const struct sockaddr *addr,
int addr_len, gpr_timespec deadline) {
SOCKET sock = INVALID_SOCKET;
BOOL success;
int status;
struct sockaddr_in6 addr6_v4mapped;
struct sockaddr_in6 local_address;
async_connect *ac;
grpc_winsocket *socket = NULL;
LPFN_CONNECTEX ConnectEx;
GUID guid = WSAID_CONNECTEX;
DWORD ioctl_num_bytes;
const char *message = NULL;
char *utf8_message;
grpc_winsocket_callback_info *info;
/* Use dualstack sockets where available. */
if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) {
addr = (const struct sockaddr *)&addr6_v4mapped;
addr_len = sizeof(addr6_v4mapped);
}
sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0,
WSA_FLAG_OVERLAPPED);
if (sock == INVALID_SOCKET) {
message = "Unable to create socket: %s";
goto failure;
}
if (!grpc_tcp_prepare_socket(sock)) {
message = "Unable to set socket options: %s";
goto failure;
}
status = WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER,
&guid, sizeof(guid), &ConnectEx, sizeof(ConnectEx),
&ioctl_num_bytes, NULL, NULL);
if (status != 0) {
message = "Unable to retreive ConnectEx pointer: %s";
goto failure;
}
grpc_sockaddr_make_wildcard6(0, &local_address);
status = bind(sock, (struct sockaddr *) &local_address,
sizeof(local_address));
if (status != 0) {
message = "Unable to bind socket: %s";
goto failure;
}
socket = grpc_winsocket_create(sock);
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 {
int error = WSAGetLastError();
if (error != ERROR_IO_PENDING) {
message = "ConnectEx failed: %s";
goto failure;
}
}
gpr_log(GPR_DEBUG, "grpc_tcp_client_connect: connection pending");
ac = gpr_malloc(sizeof(async_connect));
ac->cb = cb;
ac->cb_arg = arg;
ac->socket = socket;
gpr_mu_init(&ac->mu);
ac->refs = 2;
grpc_alarm_init(&ac->alarm, deadline, on_alarm, ac, gpr_now());
grpc_socket_notify_on_write(socket, on_connect, ac);
return;
failure:
utf8_message = gpr_format_message(WSAGetLastError());
gpr_log(GPR_ERROR, message, utf8_message);
gpr_free(utf8_message);
if (socket) {
grpc_winsocket_orphan(socket);
} else if (sock != INVALID_SOCKET) {
closesocket(sock);
}
cb(arg, NULL);
}
#endif /* GPR_WINSOCK_SOCKET */

@ -68,7 +68,7 @@ int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr,
The file descriptor remains owned by the server, and will be cleaned
up when grpc_tcp_server_destroy is called. */
int grpc_tcp_server_get_fd(grpc_tcp_server *s, int index);
int grpc_tcp_server_get_fd(grpc_tcp_server *s, unsigned index);
void grpc_tcp_server_destroy(grpc_tcp_server *server);

@ -31,11 +31,15 @@
*
*/
/* FIXME: "posix" files shouldn't be depending on _GNU_SOURCE */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <grpc/support/port_platform.h>
#ifdef GPR_POSIX_SOCKET
#define _GNU_SOURCE
#include "src/core/iomgr/tcp_server.h"
#include <limits.h>
@ -44,12 +48,14 @@
#include <netinet/tcp.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include "src/core/iomgr/pollset_posix.h"
#include "src/core/iomgr/resolve_address.h"
#include "src/core/iomgr/sockaddr_utils.h"
#include "src/core/iomgr/socket_utils_posix.h"
#include "src/core/iomgr/tcp_posix.h"
@ -69,6 +75,12 @@ typedef struct {
int fd;
grpc_fd *emfd;
grpc_tcp_server *server;
union {
gpr_uint8 untyped[GRPC_MAX_SOCKADDR_SIZE];
struct sockaddr sockaddr;
struct sockaddr_un un;
} addr;
int addr_len;
} server_port;
/* the overall server */
@ -117,6 +129,9 @@ void grpc_tcp_server_destroy(grpc_tcp_server *s) {
/* delete ALL the things */
for (i = 0; i < s->nports; i++) {
server_port *sp = &s->ports[i];
if (sp->addr.sockaddr.sa_family == AF_UNIX) {
unlink(sp->addr.un.sun_path);
}
grpc_fd_orphan(sp->emfd, NULL, NULL);
}
gpr_free(s->ports);
@ -166,8 +181,8 @@ static int prepare_socket(int fd, const struct sockaddr *addr, int addr_len) {
}
if (!grpc_set_socket_nonblocking(fd, 1) || !grpc_set_socket_cloexec(fd, 1) ||
!grpc_set_socket_low_latency(fd, 1) ||
!grpc_set_socket_reuse_addr(fd, 1)) {
(addr->sa_family != AF_UNIX && (!grpc_set_socket_low_latency(fd, 1) ||
!grpc_set_socket_reuse_addr(fd, 1)))) {
gpr_log(GPR_ERROR, "Unable to configure socket %d: %s", fd,
strerror(errno));
goto error;
@ -261,6 +276,8 @@ static int add_socket_to_server(grpc_tcp_server *s, int fd,
sp->server = s;
sp->fd = fd;
sp->emfd = grpc_fd_create(fd);
memcpy(sp->addr.untyped, addr, addr_len);
sp->addr_len = addr_len;
GPR_ASSERT(sp->emfd);
gpr_mu_unlock(&s->mu);
}
@ -272,7 +289,7 @@ int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr,
int addr_len) {
int allocated_port1 = -1;
int allocated_port2 = -1;
int i;
unsigned i;
int fd;
grpc_dualstack_mode dsmode;
struct sockaddr_in6 addr6_v4mapped;
@ -345,8 +362,8 @@ done:
return allocated_port1 >= 0 ? allocated_port1 : allocated_port2;
}
int grpc_tcp_server_get_fd(grpc_tcp_server *s, int index) {
return (0 <= index && index < s->nports) ? s->ports[index].fd : -1;
int grpc_tcp_server_get_fd(grpc_tcp_server *s, unsigned index) {
return (index < s->nports) ? s->ports[index].fd : -1;
}
void grpc_tcp_server_start(grpc_tcp_server *s, grpc_pollset *pollset,

@ -0,0 +1,374 @@
/*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING 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
#define _GNU_SOURCE
#include "src/core/iomgr/sockaddr_utils.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/log_win32.h>
#include <grpc/support/sync.h>
#include <grpc/support/time.h>
#include "src/core/iomgr/iocp_windows.h"
#include "src/core/iomgr/pollset_windows.h"
#include "src/core/iomgr/socket_windows.h"
#include "src/core/iomgr/tcp_server.h"
#include "src/core/iomgr/tcp_windows.h"
#define INIT_PORT_CAP 2
#define MIN_SAFE_ACCEPT_QUEUE_SIZE 100
static gpr_once s_init_max_accept_queue_size;
static int s_max_accept_queue_size;
/* one listening port */
typedef struct server_port {
gpr_uint8 addresses[sizeof(struct sockaddr_in6) * 2 + 32];
SOCKET new_socket;
grpc_winsocket *socket;
grpc_tcp_server *server;
LPFN_ACCEPTEX AcceptEx;
} server_port;
/* the overall server */
struct grpc_tcp_server {
grpc_tcp_server_cb cb;
void *cb_arg;
gpr_mu mu;
gpr_cv cv;
/* active port count: how many ports are actually still listening */
int active_ports;
/* all listening ports */
server_port *ports;
size_t nports;
size_t port_capacity;
};
grpc_tcp_server *grpc_tcp_server_create(void) {
grpc_tcp_server *s = gpr_malloc(sizeof(grpc_tcp_server));
gpr_mu_init(&s->mu);
gpr_cv_init(&s->cv);
s->active_ports = 0;
s->cb = NULL;
s->cb_arg = NULL;
s->ports = gpr_malloc(sizeof(server_port) * INIT_PORT_CAP);
s->nports = 0;
s->port_capacity = INIT_PORT_CAP;
return s;
}
void grpc_tcp_server_destroy(grpc_tcp_server *s) {
size_t i;
gpr_mu_lock(&s->mu);
/* shutdown all fd's */
for (i = 0; i < s->nports; i++) {
grpc_winsocket_shutdown(s->ports[i].socket);
}
/* wait while that happens */
while (s->active_ports) {
gpr_cv_wait(&s->cv, &s->mu, gpr_inf_future);
}
gpr_mu_unlock(&s->mu);
/* delete ALL the things */
for (i = 0; i < s->nports; i++) {
server_port *sp = &s->ports[i];
grpc_winsocket_orphan(sp->socket);
}
gpr_free(s->ports);
gpr_free(s);
}
/* Prepare a recently-created socket for listening. */
static int prepare_socket(SOCKET sock,
const struct sockaddr *addr, int addr_len) {
struct sockaddr_storage sockname_temp;
socklen_t sockname_len;
if (sock == INVALID_SOCKET) goto error;
if (!grpc_tcp_prepare_socket(sock)) {
char *utf8_message = gpr_format_message(WSAGetLastError());
gpr_log(GPR_ERROR, "Unable to prepare socket: %s", utf8_message);
gpr_free(utf8_message);
goto error;
}
if (bind(sock, addr, addr_len) == SOCKET_ERROR) {
char *addr_str;
char *utf8_message = gpr_format_message(WSAGetLastError());
grpc_sockaddr_to_string(&addr_str, addr, 0);
gpr_log(GPR_ERROR, "bind addr=%s: %s", addr_str, utf8_message);
gpr_free(utf8_message);
gpr_free(addr_str);
goto error;
}
if (listen(sock, SOMAXCONN) == SOCKET_ERROR) {
char *utf8_message = gpr_format_message(WSAGetLastError());
gpr_log(GPR_ERROR, "listen: %s", utf8_message);
gpr_free(utf8_message);
goto error;
}
sockname_len = sizeof(sockname_temp);
if (getsockname(sock, (struct sockaddr *) &sockname_temp, &sockname_len)
== SOCKET_ERROR) {
char *utf8_message = gpr_format_message(WSAGetLastError());
gpr_log(GPR_ERROR, "getsockname: %s", utf8_message);
gpr_free(utf8_message);
goto error;
}
return grpc_sockaddr_get_port((struct sockaddr *) &sockname_temp);
error:
if (sock != INVALID_SOCKET) closesocket(sock);
return -1;
}
static void on_accept(void *arg, int success);
static void start_accept(server_port *port) {
SOCKET sock = INVALID_SOCKET;
char *message;
char *utf8_message;
BOOL success;
DWORD addrlen = sizeof(struct sockaddr_in6) + 16;
DWORD bytes_received = 0;
sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0,
WSA_FLAG_OVERLAPPED);
if (sock == INVALID_SOCKET) {
message = "Unable to create socket: %s";
goto failure;
}
if (!grpc_tcp_prepare_socket(sock)) {
message = "Unable to prepare socket: %s";
goto failure;
}
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 {
int error = WSAGetLastError();
if (error != ERROR_IO_PENDING) {
message = "AcceptEx failed: %s";
goto failure;
}
}
port->new_socket = sock;
grpc_socket_notify_on_read(port->socket, on_accept, port);
return;
failure:
utf8_message = gpr_format_message(WSAGetLastError());
gpr_log(GPR_ERROR, message, utf8_message);
gpr_free(utf8_message);
if (sock != INVALID_SOCKET) closesocket(sock);
}
/* event manager callback when reads are ready */
static void on_accept(void *arg, int success) {
server_port *sp = arg;
SOCKET sock = sp->new_socket;
grpc_winsocket_callback_info *info = &sp->socket->read_info;
grpc_endpoint *ep = NULL;
if (success) {
DWORD transfered_bytes = 0;
DWORD flags;
BOOL wsa_success = WSAGetOverlappedResult(sock, &info->overlapped,
&transfered_bytes, FALSE,
&flags);
if (!wsa_success) {
char *utf8_message = gpr_format_message(WSAGetLastError());
gpr_log(GPR_ERROR, "on_accept error: %s", utf8_message);
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) {
gpr_cv_broadcast(&sp->server->cv);
}
gpr_mu_unlock(&sp->server->mu);
}
if (ep) sp->server->cb(sp->server->cb_arg, ep);
start_accept(sp);
}
static int add_socket_to_server(grpc_tcp_server *s, SOCKET sock,
const struct sockaddr *addr, int addr_len) {
server_port *sp;
int port;
int status;
GUID guid = WSAID_ACCEPTEX;
DWORD ioctl_num_bytes;
LPFN_ACCEPTEX AcceptEx;
if (sock == INVALID_SOCKET) return -1;
status = WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER,
&guid, sizeof(guid), &AcceptEx, sizeof(AcceptEx),
&ioctl_num_bytes, NULL, NULL);
if (status != 0) {
char *utf8_message = gpr_format_message(WSAGetLastError());
gpr_log(GPR_ERROR, "on_connect error: %s", utf8_message);
gpr_free(utf8_message);
closesocket(sock);
return -1;
}
port = prepare_socket(sock, addr, addr_len);
if (port >= 0) {
gpr_mu_lock(&s->mu);
GPR_ASSERT(!s->cb && "must add ports before starting server");
/* append it to the list under a lock */
if (s->nports == s->port_capacity) {
s->port_capacity *= 2;
s->ports = gpr_realloc(s->ports, sizeof(server_port) * s->port_capacity);
}
sp = &s->ports[s->nports++];
sp->server = s;
sp->socket = grpc_winsocket_create(sock);
sp->AcceptEx = AcceptEx;
GPR_ASSERT(sp->socket);
gpr_mu_unlock(&s->mu);
}
return port;
}
int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr,
int addr_len) {
int allocated_port = -1;
unsigned i;
SOCKET sock;
struct sockaddr_in6 addr6_v4mapped;
struct sockaddr_in6 wildcard;
struct sockaddr *allocated_addr = NULL;
struct sockaddr_storage sockname_temp;
socklen_t sockname_len;
int port;
/* Check if this is a wildcard port, and if so, try to keep the port the same
as some previously created listener. */
if (grpc_sockaddr_get_port(addr) == 0) {
for (i = 0; i < s->nports; i++) {
sockname_len = sizeof(sockname_temp);
if (0 == getsockname(s->ports[i].socket->socket,
(struct sockaddr *) &sockname_temp,
&sockname_len)) {
port = grpc_sockaddr_get_port((struct sockaddr *) &sockname_temp);
if (port > 0) {
allocated_addr = malloc(addr_len);
memcpy(allocated_addr, addr, addr_len);
grpc_sockaddr_set_port(allocated_addr, port);
addr = allocated_addr;
break;
}
}
}
}
if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) {
addr = (const struct sockaddr *)&addr6_v4mapped;
addr_len = sizeof(addr6_v4mapped);
}
/* Treat :: or 0.0.0.0 as a family-agnostic wildcard. */
if (grpc_sockaddr_is_wildcard(addr, &port)) {
grpc_sockaddr_make_wildcard6(port, &wildcard);
addr = (struct sockaddr *) &wildcard;
addr_len = sizeof(wildcard);
}
sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0,
WSA_FLAG_OVERLAPPED);
if (sock == INVALID_SOCKET) {
char *utf8_message = gpr_format_message(WSAGetLastError());
gpr_log(GPR_ERROR, "unable to create socket: %s", utf8_message);
gpr_free(utf8_message);
}
allocated_port = add_socket_to_server(s, sock, addr, addr_len);
gpr_free(allocated_addr);
return allocated_port;
}
SOCKET grpc_tcp_server_get_socket(grpc_tcp_server *s, unsigned index) {
return (index < s->nports) ? s->ports[index].socket->socket : INVALID_SOCKET;
}
void grpc_tcp_server_start(grpc_tcp_server *s, grpc_pollset *pollset,
grpc_tcp_server_cb cb, void *cb_arg) {
size_t i;
GPR_ASSERT(cb);
gpr_mu_lock(&s->mu);
GPR_ASSERT(!s->cb);
GPR_ASSERT(s->active_ports == 0);
s->cb = cb;
s->cb_arg = cb_arg;
for (i = 0; i < s->nports; i++) {
start_accept(s->ports + i);
s->active_ports++;
}
gpr_mu_unlock(&s->mu);
}
#endif /* GPR_WINSOCK_SOCKET */

@ -0,0 +1,373 @@
/*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING 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_win32.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/log_win32.h>
#include <grpc/support/slice_buffer.h>
#include <grpc/support/useful.h>
#include "src/core/iomgr/alarm.h"
#include "src/core/iomgr/iocp_windows.h"
#include "src/core/iomgr/sockaddr.h"
#include "src/core/iomgr/sockaddr_utils.h"
#include "src/core/iomgr/socket_windows.h"
#include "src/core/iomgr/tcp_client.h"
static int set_non_block(SOCKET sock) {
int status;
unsigned long param = 1;
DWORD ret;
status = WSAIoctl(sock, FIONBIO, &param, sizeof(param), NULL, 0, &ret,
NULL, NULL);
return status == 0;
}
static int set_dualstack(SOCKET sock) {
int status;
unsigned long param = 0;
status = setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
(const char *) &param, sizeof(param));
return status == 0;
}
int grpc_tcp_prepare_socket(SOCKET sock) {
if (!set_non_block(sock))
return 0;
if (!set_dualstack(sock))
return 0;
return 1;
}
typedef struct grpc_tcp {
grpc_endpoint base;
grpc_winsocket *socket;
gpr_refcount refcount;
grpc_endpoint_read_cb read_cb;
void *read_user_data;
gpr_slice read_slice;
int outstanding_read;
grpc_endpoint_write_cb write_cb;
void *write_user_data;
gpr_slice_buffer write_slices;
int outstanding_write;
} 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);
}
}
static void on_read(void *tcpp, int success) {
grpc_tcp *tcp = (grpc_tcp *) tcpp;
grpc_winsocket *socket = tcp->socket;
gpr_slice sub;
gpr_slice *slice = NULL;
size_t nslices = 0;
grpc_endpoint_cb_status status;
grpc_endpoint_read_cb cb = tcp->read_cb;
grpc_winsocket_callback_info *info = &socket->read_info;
void *opaque = tcp->read_user_data;
GPR_ASSERT(tcp->outstanding_read);
if (!success) {
tcp_unref(tcp);
cb(opaque, NULL, 0, GRPC_ENDPOINT_CB_SHUTDOWN);
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;
}
}
tcp_unref(tcp);
cb(opaque, slice, nslices, status);
}
static void win_notify_on_read(grpc_endpoint *ep,
grpc_endpoint_read_cb cb, void *arg) {
grpc_tcp *tcp = (grpc_tcp *) ep;
grpc_winsocket *handle = tcp->socket;
grpc_winsocket_callback_info *info = &handle->read_info;
int status;
DWORD bytes_read = 0;
DWORD flags = 0;
int error;
WSABUF buffer;
GPR_ASSERT(!tcp->outstanding_read);
tcp_ref(tcp);
tcp->outstanding_read = 1;
tcp->read_cb = cb;
tcp->read_user_data = arg;
tcp->read_slice = gpr_slice_malloc(8192);
buffer.len = GPR_SLICE_LENGTH(tcp->read_slice);
buffer.buf = 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;
}
error = WSAGetLastError();
if (error != WSA_IO_PENDING) {
char *utf8_message = gpr_format_message(WSAGetLastError());
__debugbreak();
gpr_log(GPR_ERROR, "WSARecv error: %s", utf8_message);
gpr_free(utf8_message);
/* would the IO completion port be called anyway... ? Let's assume not. */
tcp->outstanding_read = 0;
tcp_unref(tcp);
cb(arg, NULL, 0, GRPC_ENDPOINT_CB_ERROR);
return;
}
gpr_log(GPR_DEBUG, "waiting on the IO completion port now");
grpc_socket_notify_on_read(tcp->socket, on_read, tcp);
}
static void on_write(void *tcpp, int success) {
grpc_tcp *tcp = (grpc_tcp *) tcpp;
grpc_winsocket *handle = tcp->socket;
grpc_winsocket_callback_info *info = &handle->write_info;
grpc_endpoint_cb_status status = GRPC_ENDPOINT_CB_OK;
grpc_endpoint_write_cb cb = tcp->write_cb;
void *opaque = tcp->write_user_data;
GPR_ASSERT(tcp->outstanding_write);
gpr_log(GPR_DEBUG, "on_write");
if (!success) {
tcp_unref(tcp);
cb(opaque, GRPC_ENDPOINT_CB_SHUTDOWN);
return;
}
if (info->wsa_error != 0) {
char *utf8_message = gpr_format_message(info->wsa_error);
gpr_log(GPR_ERROR, "WSASend overlapped error: %s", utf8_message);
gpr_free(utf8_message);
status = GRPC_ENDPOINT_CB_ERROR;
} else {
GPR_ASSERT(info->bytes_transfered == tcp->write_slices.length);
}
gpr_slice_buffer_reset_and_unref(&tcp->write_slices);
tcp->outstanding_write = 0;
tcp_unref(tcp);
cb(opaque, status);
}
static grpc_endpoint_write_status win_write(grpc_endpoint *ep,
gpr_slice *slices, size_t nslices,
grpc_endpoint_write_cb cb,
void *arg) {
grpc_tcp *tcp = (grpc_tcp *) ep;
grpc_winsocket *socket = tcp->socket;
grpc_winsocket_callback_info *info = &socket->write_info;
unsigned i;
DWORD bytes_sent;
int status;
WSABUF local_buffers[16];
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;
gpr_slice_buffer_addn(&tcp->write_slices, slices, nslices);
if (tcp->write_slices.count > GPR_ARRAY_SIZE(local_buffers)) {
buffers = (WSABUF *) gpr_malloc(sizeof(WSABUF) * tcp->write_slices.count);
allocated = buffers;
}
for (i = 0; i < tcp->write_slices.count; i++) {
buffers[i].len = GPR_SLICE_LENGTH(tcp->write_slices.slices[i]);
buffers[i].buf = 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);
} else {
char *utf8_message = gpr_format_message(info->wsa_error);
gpr_log(GPR_ERROR, "WSASend error: %s", utf8_message);
gpr_free(utf8_message);
}
if (allocated) gpr_free(allocated);
gpr_slice_buffer_reset_and_unref(&tcp->write_slices);
tcp->outstanding_write = 0;
tcp_unref(tcp);
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);
if (allocated) gpr_free(allocated);
if (status != 0) {
int error = WSAGetLastError();
if (error != WSA_IO_PENDING) {
char *utf8_message = gpr_format_message(WSAGetLastError());
__debugbreak();
gpr_log(GPR_ERROR, "WSASend error: %s", utf8_message);
gpr_free(utf8_message);
/* would the IO completion port be called anyway ? Let's assume not. */
tcp->outstanding_write = 0;
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);
return GRPC_ENDPOINT_WRITE_PENDING;
}
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);
}
static grpc_endpoint_vtable vtable = {
win_notify_on_read, win_write, win_add_to_pollset, win_shutdown, win_destroy
};
grpc_endpoint *grpc_tcp_create(grpc_winsocket *socket) {
grpc_tcp *tcp = (grpc_tcp *) gpr_malloc(sizeof(grpc_tcp));
memset(tcp, 0, sizeof(grpc_tcp));
tcp->base.vtable = &vtable;
tcp->socket = socket;
gpr_slice_buffer_init(&tcp->write_slices);
gpr_ref_init(&tcp->refcount, 1);
return &tcp->base;
}
#endif /* GPR_WINSOCK_SOCKET */

@ -0,0 +1,57 @@
/*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE 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_IOMGR_TCP_WINDOWS_H__
#define __GRPC_INTERNAL_IOMGR_TCP_WINDOWS_H__
/*
Low level TCP "bottom half" implementation, for use by transports built on
top of a TCP connection.
Note that this file does not (yet) include APIs for creating the socket in
the first place.
All calls passing slice transfer ownership of a slice refcount unless
otherwise specified.
*/
#include "src/core/iomgr/endpoint.h"
#include "src/core/iomgr/socket_windows.h"
/* Create a tcp endpoint given a winsock handle.
* Takes ownership of the handle.
*/
grpc_endpoint *grpc_tcp_create(grpc_winsocket *socket);
int grpc_tcp_prepare_socket(SOCKET sock);
#endif /* __GRPC_INTERNAL_IOMGR_TCP_WINDOWS_H__ */

@ -0,0 +1,82 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <grpc/support/port_platform.h>
#ifdef GPR_LINUX_EVENTFD
#include <errno.h>
#include <sys/eventfd.h>
#include <unistd.h>
#include "src/core/iomgr/wakeup_fd_posix.h"
#include <grpc/support/log.h>
static void eventfd_create(grpc_wakeup_fd_info *fd_info) {
int efd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
/* TODO(klempner): Handle failure more gracefully */
GPR_ASSERT(efd >= 0);
fd_info->read_fd = efd;
fd_info->write_fd = -1;
}
static void eventfd_consume(grpc_wakeup_fd_info *fd_info) {
eventfd_t value;
int err;
do {
err = eventfd_read(fd_info->read_fd, &value);
} while (err < 0 && errno == EINTR);
}
static void eventfd_wakeup(grpc_wakeup_fd_info *fd_info) {
int err;
do {
err = eventfd_write(fd_info->read_fd, 1);
} while (err < 0 && errno == EINTR);
}
static void eventfd_destroy(grpc_wakeup_fd_info *fd_info) {
close(fd_info->read_fd);
}
static int eventfd_check_availability(void) {
/* TODO(klempner): Actually check if eventfd is available */
return 1;
}
const grpc_wakeup_fd_vtable grpc_specialized_wakeup_fd_vtable = {
eventfd_create, eventfd_consume, eventfd_wakeup, eventfd_destroy,
eventfd_check_availability
};
#endif /* GPR_LINUX_EVENTFD */

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

@ -0,0 +1,97 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <grpc/support/port_platform.h>
#ifdef GPR_POSIX_WAKEUP_FD
#include "src/core/iomgr/wakeup_fd_posix.h"
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include "src/core/iomgr/socket_utils_posix.h"
#include <grpc/support/log.h>
static void pipe_create(grpc_wakeup_fd_info *fd_info) {
int pipefd[2];
/* TODO(klempner): Make this nonfatal */
GPR_ASSERT(0 == pipe(pipefd));
GPR_ASSERT(grpc_set_socket_nonblocking(pipefd[0], 1));
GPR_ASSERT(grpc_set_socket_nonblocking(pipefd[1], 1));
fd_info->read_fd = pipefd[0];
fd_info->write_fd = pipefd[1];
}
static void pipe_consume(grpc_wakeup_fd_info *fd_info) {
char buf[128];
int r;
for (;;) {
r = read(fd_info->read_fd, buf, sizeof(buf));
if (r > 0) continue;
if (r == 0) return;
switch (errno) {
case EAGAIN:
return;
case EINTR:
continue;
default:
gpr_log(GPR_ERROR, "error reading pipe: %s", strerror(errno));
return;
}
}
}
static void pipe_wakeup(grpc_wakeup_fd_info *fd_info) {
char c = 0;
while (write(fd_info->write_fd, &c, 1) != 1 && errno == EINTR)
;
}
static void pipe_destroy(grpc_wakeup_fd_info *fd_info) {
close(fd_info->read_fd);
close(fd_info->write_fd);
}
static int pipe_check_availability(void) {
/* Assume that pipes are always available. */
return 1;
}
const grpc_wakeup_fd_vtable grpc_pipe_wakeup_fd_vtable = {
pipe_create, pipe_consume, pipe_wakeup, pipe_destroy, pipe_check_availability
};
#endif /* GPR_POSIX_WAKUP_FD */

@ -1,6 +1,6 @@
/*
*
* Copyright 2014, Google Inc.
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -31,13 +31,11 @@
*
*/
#ifndef __GRPC_SUPPORT_TIME_POSIX_H__
#define __GRPC_SUPPORT_TIME_POSIX_H__
/* Posix variant of gpr_time_platform.h */
#ifndef __GRPC_INTERNAL_IOMGR_WAKEUP_FD_PIPE_H_
#define __GRPC_INTERNAL_IOMGR_WAKEUP_FD_PIPE_H_
#include <sys/time.h>
#include <time.h>
#include "src/core/iomgr/wakeup_fd_posix.h"
typedef struct timespec gpr_timespec;
extern grpc_wakeup_fd_vtable grpc_pipe_wakeup_fd_vtable;
#endif /* __GRPC_SUPPORT_TIME_POSIX_H__ */
#endif /* __GRPC_INTERNAL_IOMGR_WAKEUP_FD_PIPE_H_ */

@ -0,0 +1,76 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <grpc/support/port_platform.h>
#ifdef GPR_POSIX_WAKEUP_FD
#include "src/core/iomgr/wakeup_fd_posix.h"
#include "src/core/iomgr/wakeup_fd_pipe.h"
#include <stddef.h>
static const grpc_wakeup_fd_vtable *wakeup_fd_vtable = NULL;
void grpc_wakeup_fd_global_init(void) {
if (grpc_specialized_wakeup_fd_vtable.check_availability()) {
wakeup_fd_vtable = &grpc_specialized_wakeup_fd_vtable;
} else {
wakeup_fd_vtable = &grpc_pipe_wakeup_fd_vtable;
}
}
void grpc_wakeup_fd_global_init_force_fallback(void) {
wakeup_fd_vtable = &grpc_pipe_wakeup_fd_vtable;
}
void grpc_wakeup_fd_global_destroy(void) {
wakeup_fd_vtable = NULL;
}
void grpc_wakeup_fd_create(grpc_wakeup_fd_info *fd_info) {
wakeup_fd_vtable->create(fd_info);
}
void grpc_wakeup_fd_consume_wakeup(grpc_wakeup_fd_info *fd_info) {
wakeup_fd_vtable->consume(fd_info);
}
void grpc_wakeup_fd_wakeup(grpc_wakeup_fd_info *fd_info) {
wakeup_fd_vtable->wakeup(fd_info);
}
void grpc_wakeup_fd_destroy(grpc_wakeup_fd_info *fd_info) {
wakeup_fd_vtable->destroy(fd_info);
}
#endif /* GPR_POSIX_WAKEUP_FD */

@ -0,0 +1,99 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/*
* wakeup_fd abstracts the concept of a file descriptor for the purpose of
* waking up a thread in select()/poll()/epoll_wait()/etc.
* The poll() family of system calls provide a way for a thread to block until
* there is activity on one (or more) of a set of file descriptors. An
* application may wish to wake up this thread to do non file related work. The
* typical way to do this is to add a pipe to the set of file descriptors, then
* write to the pipe to wake up the thread in poll().
*
* Linux has a lighter weight eventfd specifically designed for this purpose.
* wakeup_fd abstracts the difference between the two.
*
* Setup:
* 1. Before calling anything, call global_init() at least once.
* 1. Call grpc_wakeup_fd_create() to get a wakeup_fd.
* 2. Add the result of GRPC_WAKEUP_FD_FD to the set of monitored file
* descriptors for the poll() style API you are using. Monitor the file
* descriptor for readability.
* 3. To tear down, call grpc_wakeup_fd_destroy(). This closes the underlying
* file descriptor.
*
* Usage:
* 1. To wake up a polling thread, call grpc_wakeup_fd_wakeup() on a wakeup_fd
* it is monitoring.
* 2. If the polling thread was awakened by a wakeup_fd event, call
* grpc_wakeup_fd_consume_wakeup() on it.
*/
#ifndef __GRPC_INTERNAL_IOMGR_WAKEUP_FD_POSIX_H_
#define __GRPC_INTERNAL_IOMGR_WAKEUP_FD_POSIX_H_
void grpc_wakeup_fd_global_init(void);
void grpc_wakeup_fd_global_destroy(void);
/* Force using the fallback implementation. This is intended for testing
* purposes only.*/
void grpc_wakeup_fd_global_init_force_fallback(void);
typedef struct grpc_wakeup_fd_info grpc_wakeup_fd_info;
typedef struct grpc_wakeup_fd_vtable {
void (*create)(grpc_wakeup_fd_info *fd_info);
void (*consume)(grpc_wakeup_fd_info *fd_info);
void (*wakeup)(grpc_wakeup_fd_info *fd_info);
void (*destroy)(grpc_wakeup_fd_info *fd_info);
/* Must be called before calling any other functions */
int (*check_availability)(void);
} grpc_wakeup_fd_vtable;
struct grpc_wakeup_fd_info {
int read_fd;
int write_fd;
};
#define GRPC_WAKEUP_FD_GET_READ_FD(fd_info) ((fd_info)->read_fd)
void grpc_wakeup_fd_create(grpc_wakeup_fd_info *fd_info);
void grpc_wakeup_fd_consume_wakeup(grpc_wakeup_fd_info *fd_info);
void grpc_wakeup_fd_wakeup(grpc_wakeup_fd_info *fd_info);
void grpc_wakeup_fd_destroy(grpc_wakeup_fd_info *fd_info);
/* Defined in some specialized implementation's .c file, or by
* wakeup_fd_nospecial.c if no such implementation exists. */
extern const grpc_wakeup_fd_vtable grpc_specialized_wakeup_fd_vtable;
#endif /* __GRPC_INTERNAL_IOMGR_WAKEUP_FD_POSIX_H_ */

@ -31,30 +31,34 @@
*
*/
#include <grpc++/client_context.h>
#include <string.h>
#include "examples/tips/client.h"
#include <grpc/support/alloc.h>
using tech::pubsub::Topic;
using tech::pubsub::PublisherService;
#include "src/core/json/json.h"
namespace grpc {
namespace examples {
namespace tips {
grpc_json *grpc_json_create(grpc_json_type type) {
grpc_json *json = gpr_malloc(sizeof(grpc_json));
memset(json, 0, sizeof(grpc_json));
json->type = type;
Client::Client(std::shared_ptr<ChannelInterface> channel)
: stub_(PublisherService::NewStub(channel)) {
return json;
}
Status Client::CreateTopic(grpc::string topic) {
Topic request;
Topic response;
request.set_name(topic);
ClientContext context;
void grpc_json_destroy(grpc_json *json) {
while (json->child) {
grpc_json_destroy(json->child);
}
return stub_->CreateTopic(&context, request, &response);
}
if (json->next) {
json->next->prev = json->prev;
}
if (json->prev) {
json->prev->next = json->next;
} else if (json->parent) {
json->parent->child = json->next;
}
} // namespace tips
} // namespace examples
} // namespace grpc
gpr_free(json);
}

@ -0,0 +1,88 @@
/*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef __GRPC_SRC_CORE_JSON_JSON_H__
#define __GRPC_SRC_CORE_JSON_JSON_H__
#include <stdlib.h>
#include "src/core/json/json_common.h"
/* A tree-like structure to hold json values. The key and value pointers
* are not owned by it.
*/
typedef struct grpc_json {
struct grpc_json* next;
struct grpc_json* prev;
struct grpc_json* child;
struct grpc_json* parent;
grpc_json_type type;
const char* key;
const char* value;
} grpc_json;
/* The next two functions are going to parse the input string, and
* destroy it in the process, in order to use its space to store
* all of the keys and values for the returned object tree.
*
* They assume UTF-8 input stream, and will output UTF-8 encoded
* strings in the tree. The input stream's UTF-8 isn't validated,
* as in, what you input is what you get as an output.
*
* All the keys and values in the grpc_json_t objects will be strings
* pointing at your input buffer.
*
* Delete the allocated tree afterward using grpc_json_destroy().
*/
grpc_json* grpc_json_parse_string_with_len(char* input, size_t size);
grpc_json* grpc_json_parse_string(char* input);
/* This function will create a new string using gpr_realloc, and will
* deserialize the grpc_json tree into it. It'll be zero-terminated,
* but will be allocated in chunks of 256 bytes.
*
* The indent parameter controls the way the output is formatted.
* If indent is 0, then newlines will be suppressed as well, and the
* output will be condensed at its maximum.
*/
char* grpc_json_dump_to_string(grpc_json* json, int indent);
/* Use these to create or delete a grpc_json object.
* Deletion is recursive. We will not attempt to free any of the strings
* in any of the objects of that tree.
*/
grpc_json* grpc_json_create(grpc_json_type type);
void grpc_json_destroy(grpc_json* json);
#endif /* __GRPC_SRC_CORE_JSON_JSON_H__ */

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

@ -0,0 +1,653 @@
/*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <string.h>
#include <grpc/support/port_platform.h>
#include "src/core/json/json_reader.h"
static void json_reader_string_clear(grpc_json_reader* reader) {
reader->vtable->string_clear(reader->userdata);
}
static void json_reader_string_add_char(grpc_json_reader* reader,
gpr_uint32 c) {
reader->vtable->string_add_char(reader->userdata, c);
}
static void json_reader_string_add_utf32(grpc_json_reader* reader,
gpr_uint32 utf32) {
reader->vtable->string_add_utf32(reader->userdata, utf32);
}
static gpr_uint32
grpc_json_reader_read_char(grpc_json_reader* reader) {
return reader->vtable->read_char(reader->userdata);
}
static void json_reader_container_begins(grpc_json_reader* reader,
grpc_json_type type) {
reader->vtable->container_begins(reader->userdata, type);
}
static grpc_json_type
grpc_json_reader_container_ends(grpc_json_reader* reader) {
return reader->vtable->container_ends(reader->userdata);
}
static void json_reader_set_key(grpc_json_reader* reader) {
reader->vtable->set_key(reader->userdata);
}
static void json_reader_set_string(grpc_json_reader* reader) {
reader->vtable->set_string(reader->userdata);
}
static int json_reader_set_number(grpc_json_reader* reader) {
return reader->vtable->set_number(reader->userdata);
}
static void json_reader_set_true(grpc_json_reader* reader) {
reader->vtable->set_true(reader->userdata);
}
static void json_reader_set_false(grpc_json_reader* reader) {
reader->vtable->set_false(reader->userdata);
}
static void json_reader_set_null(grpc_json_reader* reader) {
reader->vtable->set_null(reader->userdata);
}
/* Call this function to initialize the reader structure. */
void grpc_json_reader_init(grpc_json_reader* reader,
grpc_json_reader_vtable* vtable, void* userdata) {
memset(reader, 0, sizeof(grpc_json_reader));
reader->vtable = vtable;
reader->userdata = userdata;
json_reader_string_clear(reader);
reader->state = GRPC_JSON_STATE_VALUE_BEGIN;
}
int grpc_json_reader_is_complete(grpc_json_reader* reader) {
return ((reader->depth == 0) && ((reader->state == GRPC_JSON_STATE_END) ||
(reader->state == GRPC_JSON_STATE_VALUE_END)));
}
grpc_json_reader_status grpc_json_reader_run(grpc_json_reader* reader) {
gpr_uint32 c, success;
/* This state-machine is a strict implementation of ECMA-404 */
for (;;) {
c = grpc_json_reader_read_char(reader);
switch (c) {
/* Let's process the error cases first. */
case GRPC_JSON_READ_CHAR_ERROR:
return GRPC_JSON_READ_ERROR;
case GRPC_JSON_READ_CHAR_EAGAIN:
return GRPC_JSON_EAGAIN;
case GRPC_JSON_READ_CHAR_EOF:
if (grpc_json_reader_is_complete(reader)) {
return GRPC_JSON_DONE;
} else {
return GRPC_JSON_PARSE_ERROR;
}
break;
/* Processing whitespaces. */
case ' ':
case '\t':
case '\n':
case '\r':
switch (reader->state) {
case GRPC_JSON_STATE_OBJECT_KEY_BEGIN:
case GRPC_JSON_STATE_OBJECT_KEY_END:
case GRPC_JSON_STATE_VALUE_BEGIN:
case GRPC_JSON_STATE_VALUE_END:
case GRPC_JSON_STATE_END:
break;
case GRPC_JSON_STATE_OBJECT_KEY_STRING:
case GRPC_JSON_STATE_VALUE_STRING:
if (c != ' ') return GRPC_JSON_PARSE_ERROR;
if (reader->unicode_high_surrogate != 0) return GRPC_JSON_PARSE_ERROR;
json_reader_string_add_char(reader, c);
break;
case GRPC_JSON_STATE_VALUE_NUMBER:
case GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL:
case GRPC_JSON_STATE_VALUE_NUMBER_ZERO:
case GRPC_JSON_STATE_VALUE_NUMBER_EPM:
success = json_reader_set_number(reader);
if (!success) return GRPC_JSON_PARSE_ERROR;
json_reader_string_clear(reader);
reader->state = GRPC_JSON_STATE_VALUE_END;
break;
default:
return GRPC_JSON_PARSE_ERROR;
}
break;
/* Value, object or array terminations. */
case ',':
case '}':
case ']':
switch (reader->state) {
case GRPC_JSON_STATE_OBJECT_KEY_STRING:
case GRPC_JSON_STATE_VALUE_STRING:
if (reader->unicode_high_surrogate != 0) return GRPC_JSON_PARSE_ERROR;
json_reader_string_add_char(reader, c);
break;
case GRPC_JSON_STATE_VALUE_NUMBER:
case GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL:
case GRPC_JSON_STATE_VALUE_NUMBER_ZERO:
case GRPC_JSON_STATE_VALUE_NUMBER_EPM:
success = json_reader_set_number(reader);
if (!success) return GRPC_JSON_PARSE_ERROR;
json_reader_string_clear(reader);
reader->state = GRPC_JSON_STATE_VALUE_END;
/* The missing break here is intentional. */
case GRPC_JSON_STATE_VALUE_END:
case GRPC_JSON_STATE_OBJECT_KEY_BEGIN:
case GRPC_JSON_STATE_VALUE_BEGIN:
if (c == ',') {
if (reader->state != GRPC_JSON_STATE_VALUE_END) {
return GRPC_JSON_PARSE_ERROR;
}
if (reader->in_object) {
reader->state = GRPC_JSON_STATE_OBJECT_KEY_BEGIN;
} else {
reader->state = GRPC_JSON_STATE_VALUE_BEGIN;
}
} else {
if (reader->depth-- == 0) return GRPC_JSON_PARSE_ERROR;
if ((c == '}') && !reader->in_object) {
return GRPC_JSON_PARSE_ERROR;
}
if ((c == '}') &&
(reader->state == GRPC_JSON_STATE_OBJECT_KEY_BEGIN) &&
!reader->container_just_begun) {
return GRPC_JSON_PARSE_ERROR;
}
if ((c == ']') && !reader->in_array) return GRPC_JSON_PARSE_ERROR;
if ((c == ']') &&
(reader->state == GRPC_JSON_STATE_VALUE_BEGIN) &&
!reader->container_just_begun) {
return GRPC_JSON_PARSE_ERROR;
}
reader->state = GRPC_JSON_STATE_VALUE_END;
switch (grpc_json_reader_container_ends(reader)) {
case GRPC_JSON_OBJECT:
reader->in_object = 1;
reader->in_array = 0;
break;
case GRPC_JSON_ARRAY:
reader->in_object = 0;
reader->in_array = 1;
break;
case GRPC_JSON_TOP_LEVEL:
if (reader->depth != 0) return GRPC_JSON_INTERNAL_ERROR;
reader->in_object = 0;
reader->in_array = 0;
reader->state = GRPC_JSON_STATE_END;
break;
default:
return GRPC_JSON_INTERNAL_ERROR;
}
}
break;
default:
return GRPC_JSON_PARSE_ERROR;
}
break;
/* In-string escaping. */
case '\\':
switch (reader->state) {
case GRPC_JSON_STATE_OBJECT_KEY_STRING:
reader->escaped_string_was_key = 1;
reader->state = GRPC_JSON_STATE_STRING_ESCAPE;
break;
case GRPC_JSON_STATE_VALUE_STRING:
reader->escaped_string_was_key = 0;
reader->state = GRPC_JSON_STATE_STRING_ESCAPE;
break;
/* This is the \\ case. */
case GRPC_JSON_STATE_STRING_ESCAPE:
if (reader->unicode_high_surrogate != 0) return GRPC_JSON_PARSE_ERROR;
json_reader_string_add_char(reader, '\\');
if (reader->escaped_string_was_key) {
reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING;
} else {
reader->state = GRPC_JSON_STATE_VALUE_STRING;
}
break;
default:
return GRPC_JSON_PARSE_ERROR;
}
break;
default:
reader->container_just_begun = 0;
switch (reader->state) {
case GRPC_JSON_STATE_OBJECT_KEY_BEGIN:
if (c != '"') return GRPC_JSON_PARSE_ERROR;
reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING;
break;
case GRPC_JSON_STATE_OBJECT_KEY_STRING:
if (reader->unicode_high_surrogate != 0) return GRPC_JSON_PARSE_ERROR;
if (c == '"') {
reader->state = GRPC_JSON_STATE_OBJECT_KEY_END;
json_reader_set_key(reader);
json_reader_string_clear(reader);
} else {
if (c <= 0x001f) return GRPC_JSON_PARSE_ERROR;
json_reader_string_add_char(reader, c);
}
break;
case GRPC_JSON_STATE_VALUE_STRING:
if (reader->unicode_high_surrogate != 0) return GRPC_JSON_PARSE_ERROR;
if (c == '"') {
reader->state = GRPC_JSON_STATE_VALUE_END;
json_reader_set_string(reader);
json_reader_string_clear(reader);
} else {
if (c < 32) return GRPC_JSON_PARSE_ERROR;
json_reader_string_add_char(reader, c);
}
break;
case GRPC_JSON_STATE_OBJECT_KEY_END:
if (c != ':') return GRPC_JSON_PARSE_ERROR;
reader->state = GRPC_JSON_STATE_VALUE_BEGIN;
break;
case GRPC_JSON_STATE_VALUE_BEGIN:
switch (c) {
case 't':
reader->state = GRPC_JSON_STATE_VALUE_TRUE_R;
break;
case 'f':
reader->state = GRPC_JSON_STATE_VALUE_FALSE_A;
break;
case 'n':
reader->state = GRPC_JSON_STATE_VALUE_NULL_U;
break;
case '"':
reader->state = GRPC_JSON_STATE_VALUE_STRING;
break;
case '0':
json_reader_string_add_char(reader, c);
reader->state = GRPC_JSON_STATE_VALUE_NUMBER_ZERO;
break;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '-':
json_reader_string_add_char(reader, c);
reader->state = GRPC_JSON_STATE_VALUE_NUMBER;
break;
case '{':
reader->container_just_begun = 1;
json_reader_container_begins(reader, GRPC_JSON_OBJECT);
reader->depth++;
reader->state = GRPC_JSON_STATE_OBJECT_KEY_BEGIN;
reader->in_object = 1;
reader->in_array = 0;
break;
case '[':
reader->container_just_begun = 1;
json_reader_container_begins(reader, GRPC_JSON_ARRAY);
reader->depth++;
reader->in_object = 0;
reader->in_array = 1;
break;
}
break;
case GRPC_JSON_STATE_STRING_ESCAPE:
if (reader->escaped_string_was_key) {
reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING;
} else {
reader->state = GRPC_JSON_STATE_VALUE_STRING;
}
if (reader->unicode_high_surrogate && c != 'u')
return GRPC_JSON_PARSE_ERROR;
switch (c) {
case '"':
case '/':
json_reader_string_add_char(reader, c);
break;
case 'b':
json_reader_string_add_char(reader, '\b');
break;
case 'f':
json_reader_string_add_char(reader, '\f');
break;
case 'n':
json_reader_string_add_char(reader, '\n');
break;
case 'r':
json_reader_string_add_char(reader, '\r');
break;
case 't':
json_reader_string_add_char(reader, '\t');
break;
case 'u':
reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U1;
reader->unicode_char = 0;
break;
default:
return GRPC_JSON_PARSE_ERROR;
}
break;
case GRPC_JSON_STATE_STRING_ESCAPE_U1:
case GRPC_JSON_STATE_STRING_ESCAPE_U2:
case GRPC_JSON_STATE_STRING_ESCAPE_U3:
case GRPC_JSON_STATE_STRING_ESCAPE_U4:
if ((c >= '0') && (c <= '9')) {
c -= '0';
} else if ((c >= 'A') && (c <= 'F')) {
c -= 'A' - 10;
} else if ((c >= 'a') && (c <= 'f')) {
c -= 'a' - 10;
} else {
return GRPC_JSON_PARSE_ERROR;
}
reader->unicode_char <<= 4;
reader->unicode_char |= c;
switch (reader->state) {
case GRPC_JSON_STATE_STRING_ESCAPE_U1:
reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U2;
break;
case GRPC_JSON_STATE_STRING_ESCAPE_U2:
reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U3;
break;
case GRPC_JSON_STATE_STRING_ESCAPE_U3:
reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U4;
break;
case GRPC_JSON_STATE_STRING_ESCAPE_U4:
/* See grpc_json_writer_escape_string to have a description
* of what's going on here.
*/
if ((reader->unicode_char & 0xfc00) == 0xd800) {
/* high surrogate utf-16 */
if (reader->unicode_high_surrogate != 0)
return GRPC_JSON_PARSE_ERROR;
reader->unicode_high_surrogate = reader->unicode_char;
} else if ((reader->unicode_char & 0xfc00) == 0xdc00) {
/* low surrogate utf-16 */
gpr_uint32 utf32;
if (reader->unicode_high_surrogate == 0)
return GRPC_JSON_PARSE_ERROR;
utf32 = 0x10000;
utf32 += (reader->unicode_high_surrogate - 0xd800) * 0x400;
utf32 += reader->unicode_char - 0xdc00;
json_reader_string_add_utf32(reader, utf32);
reader->unicode_high_surrogate = 0;
} else {
/* anything else */
if (reader->unicode_high_surrogate != 0)
return GRPC_JSON_PARSE_ERROR;
json_reader_string_add_utf32(reader, reader->unicode_char);
}
if (reader->escaped_string_was_key) {
reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING;
} else {
reader->state = GRPC_JSON_STATE_VALUE_STRING;
}
break;
default:
return GRPC_JSON_INTERNAL_ERROR;
}
break;
case GRPC_JSON_STATE_VALUE_NUMBER:
json_reader_string_add_char(reader, c);
switch (c) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
break;
case 'e':
case 'E':
reader->state = GRPC_JSON_STATE_VALUE_NUMBER_E;
break;
case '.':
reader->state = GRPC_JSON_STATE_VALUE_NUMBER_DOT;
break;
default:
return GRPC_JSON_PARSE_ERROR;
}
break;
case GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL:
json_reader_string_add_char(reader, c);
switch (c) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
break;
case 'e':
case 'E':
reader->state = GRPC_JSON_STATE_VALUE_NUMBER_E;
break;
default:
return GRPC_JSON_PARSE_ERROR;
}
break;
case GRPC_JSON_STATE_VALUE_NUMBER_ZERO:
if (c != '.') return GRPC_JSON_PARSE_ERROR;
json_reader_string_add_char(reader, c);
reader->state = GRPC_JSON_STATE_VALUE_NUMBER_DOT;
break;
case GRPC_JSON_STATE_VALUE_NUMBER_DOT:
json_reader_string_add_char(reader, c);
switch (c) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
reader->state = GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL;
break;
default:
return GRPC_JSON_PARSE_ERROR;
}
break;
case GRPC_JSON_STATE_VALUE_NUMBER_E:
json_reader_string_add_char(reader, c);
switch (c) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '+':
case '-':
reader->state = GRPC_JSON_STATE_VALUE_NUMBER_EPM;
break;
default:
return GRPC_JSON_PARSE_ERROR;
}
break;
case GRPC_JSON_STATE_VALUE_NUMBER_EPM:
json_reader_string_add_char(reader, c);
switch (c) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
break;
default:
return GRPC_JSON_PARSE_ERROR;
}
break;
case GRPC_JSON_STATE_VALUE_TRUE_R:
if (c != 'r') return GRPC_JSON_PARSE_ERROR;
reader->state = GRPC_JSON_STATE_VALUE_TRUE_U;
break;
case GRPC_JSON_STATE_VALUE_TRUE_U:
if (c != 'u') return GRPC_JSON_PARSE_ERROR;
reader->state = GRPC_JSON_STATE_VALUE_TRUE_E;
break;
case GRPC_JSON_STATE_VALUE_TRUE_E:
if (c != 'e') return GRPC_JSON_PARSE_ERROR;
json_reader_set_true(reader);
reader->state = GRPC_JSON_STATE_VALUE_END;
break;
case GRPC_JSON_STATE_VALUE_FALSE_A:
if (c != 'a') return GRPC_JSON_PARSE_ERROR;
reader->state = GRPC_JSON_STATE_VALUE_FALSE_L;
break;
case GRPC_JSON_STATE_VALUE_FALSE_L:
if (c != 'l') return GRPC_JSON_PARSE_ERROR;
reader->state = GRPC_JSON_STATE_VALUE_FALSE_S;
break;
case GRPC_JSON_STATE_VALUE_FALSE_S:
if (c != 's') return GRPC_JSON_PARSE_ERROR;
reader->state = GRPC_JSON_STATE_VALUE_FALSE_E;
break;
case GRPC_JSON_STATE_VALUE_FALSE_E:
if (c != 'e') return GRPC_JSON_PARSE_ERROR;
json_reader_set_false(reader);
reader->state = GRPC_JSON_STATE_VALUE_END;
break;
case GRPC_JSON_STATE_VALUE_NULL_U:
if (c != 'u') return GRPC_JSON_PARSE_ERROR;
reader->state = GRPC_JSON_STATE_VALUE_NULL_L1;
break;
case GRPC_JSON_STATE_VALUE_NULL_L1:
if (c != 'l') return GRPC_JSON_PARSE_ERROR;
reader->state = GRPC_JSON_STATE_VALUE_NULL_L2;
break;
case GRPC_JSON_STATE_VALUE_NULL_L2:
if (c != 'l') return GRPC_JSON_PARSE_ERROR;
json_reader_set_null(reader);
reader->state = GRPC_JSON_STATE_VALUE_END;
break;
/* All of the VALUE_END cases are handled in the specialized case
* above. */
case GRPC_JSON_STATE_VALUE_END:
switch (c) {
case ',':
case '}':
case ']':
return GRPC_JSON_INTERNAL_ERROR;
break;
default:
return GRPC_JSON_PARSE_ERROR;
}
break;
case GRPC_JSON_STATE_END:
return GRPC_JSON_PARSE_ERROR;
}
}
}
return GRPC_JSON_INTERNAL_ERROR;
}

@ -0,0 +1,160 @@
/*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef __GRPC_SRC_CORE_JSON_JSON_READER_H__
#define __GRPC_SRC_CORE_JSON_JSON_READER_H__
#include <grpc/support/port_platform.h>
#include "src/core/json/json_common.h"
typedef enum {
GRPC_JSON_STATE_OBJECT_KEY_BEGIN,
GRPC_JSON_STATE_OBJECT_KEY_STRING,
GRPC_JSON_STATE_OBJECT_KEY_END,
GRPC_JSON_STATE_VALUE_BEGIN,
GRPC_JSON_STATE_VALUE_STRING,
GRPC_JSON_STATE_STRING_ESCAPE,
GRPC_JSON_STATE_STRING_ESCAPE_U1,
GRPC_JSON_STATE_STRING_ESCAPE_U2,
GRPC_JSON_STATE_STRING_ESCAPE_U3,
GRPC_JSON_STATE_STRING_ESCAPE_U4,
GRPC_JSON_STATE_VALUE_NUMBER,
GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL,
GRPC_JSON_STATE_VALUE_NUMBER_ZERO,
GRPC_JSON_STATE_VALUE_NUMBER_DOT,
GRPC_JSON_STATE_VALUE_NUMBER_E,
GRPC_JSON_STATE_VALUE_NUMBER_EPM,
GRPC_JSON_STATE_VALUE_TRUE_R,
GRPC_JSON_STATE_VALUE_TRUE_U,
GRPC_JSON_STATE_VALUE_TRUE_E,
GRPC_JSON_STATE_VALUE_FALSE_A,
GRPC_JSON_STATE_VALUE_FALSE_L,
GRPC_JSON_STATE_VALUE_FALSE_S,
GRPC_JSON_STATE_VALUE_FALSE_E,
GRPC_JSON_STATE_VALUE_NULL_U,
GRPC_JSON_STATE_VALUE_NULL_L1,
GRPC_JSON_STATE_VALUE_NULL_L2,
GRPC_JSON_STATE_VALUE_END,
GRPC_JSON_STATE_END
} grpc_json_reader_state;
enum {
/* The first non-unicode value is 0x110000. But let's pick
* a value high enough to start our error codes from. These
* values are safe to return from the read_char function.
*/
GRPC_JSON_READ_CHAR_EOF = 0x7ffffff0,
GRPC_JSON_READ_CHAR_EAGAIN,
GRPC_JSON_READ_CHAR_ERROR
};
struct grpc_json_reader;
typedef struct grpc_json_reader_vtable {
/* Clears your internal string scratchpad. */
void (*string_clear)(void* userdata);
/* Adds a char to the string scratchpad. */
void (*string_add_char)(void* userdata, gpr_uint32 c);
/* Adds a utf32 char to the string scratchpad. */
void (*string_add_utf32)(void* userdata, gpr_uint32 c);
/* Reads a character from your input. May be utf-8, 16 or 32. */
gpr_uint32 (*read_char)(void* userdata);
/* Starts a container of type GRPC_JSON_ARRAY or GRPC_JSON_OBJECT. */
void (*container_begins)(void* userdata, grpc_json_type type);
/* Ends the current container. Must return the type of its parent. */
grpc_json_type (*container_ends)(void* userdata);
/* Your internal string scratchpad is an object's key. */
void (*set_key)(void* userdata);
/* Your internal string scratchpad is a string value. */
void (*set_string)(void* userdata);
/* Your internal string scratchpad is a numerical value. Return 1 if valid. */
int (*set_number)(void* userdata);
/* Sets the values true, false or null. */
void (*set_true)(void* userdata);
void (*set_false)(void* userdata);
void (*set_null)(void* userdata);
} grpc_json_reader_vtable;
typedef struct grpc_json_reader {
/* That structure is fully private, and initialized by grpc_json_reader_init.
* The definition is public so you can put it on your stack.
*/
void* userdata;
grpc_json_reader_vtable* vtable;
int depth;
int in_object;
int in_array;
int escaped_string_was_key;
int container_just_begun;
gpr_uint16 unicode_char, unicode_high_surrogate;
grpc_json_reader_state state;
} grpc_json_reader;
/* The return type of the parser. */
typedef enum {
GRPC_JSON_DONE, /* The parser finished successfully. */
GRPC_JSON_EAGAIN, /* The parser yields to get more data. */
GRPC_JSON_READ_ERROR, /* The parser passes through a read error. */
GRPC_JSON_PARSE_ERROR, /* The parser found an error in the json stream. */
GRPC_JSON_INTERNAL_ERROR /* The parser got an internal error. */
} grpc_json_reader_status;
/* Call this function to start parsing the input. It will return the following:
* . GRPC_JSON_DONE if the input got eof, and the parsing finished
* successfully.
* . GRPC_JSON_EAGAIN if the read_char function returned again. Call the
* parser again as needed. It is okay to call the parser in polling mode,
* although a bit dull.
* . GRPC_JSON_READ_ERROR if the read_char function returned an error. The
* state isn't broken however, and the function can be called again if the
* error has been corrected. But please use the EAGAIN feature instead for
* consistency.
* . GRPC_JSON_PARSE_ERROR if the input was somehow invalid.
* . GRPC_JSON_INTERNAL_ERROR if the parser somehow ended into an invalid
* internal state.
*/
grpc_json_reader_status grpc_json_reader_run(grpc_json_reader* reader);
/* Call this function to initialize the reader structure. */
void grpc_json_reader_init(grpc_json_reader* reader,
grpc_json_reader_vtable* vtable, void* userdata);
/* You may call this from the read_char callback if you don't know where is the
* end of your input stream, and you'd like the json reader to hint you that it
* has completed reading its input, so you can return an EOF to it. Note that
* there might still be trailing whitespaces after that point.
*/
int grpc_json_reader_is_complete(grpc_json_reader* reader);
#endif /* __GRPC_SRC_CORE_JSON_JSON_READER_H__ */

@ -0,0 +1,391 @@
/*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <string.h>
#include <stdlib.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include "src/core/json/json.h"
#include "src/core/json/json_reader.h"
#include "src/core/json/json_writer.h"
/* The json reader will construct a bunch of grpc_json objects and
* link them all up together in a tree-like structure that will represent
* the json data in memory.
*
* It also uses its own input as a scratchpad to store all of the decoded,
* unescaped strings. So we need to keep track of all these pointers in
* that opaque structure the reader will carry for us.
*
* Note that this works because the act of parsing json always reduces its
* input size, and never expands it.
*/
typedef struct {
grpc_json* top;
grpc_json* current_container;
grpc_json* current_value;
gpr_uint8* input;
gpr_uint8* key;
gpr_uint8* string;
gpr_uint8* string_ptr;
size_t remaining_input;
} json_reader_userdata;
/* This json writer will put everything in a big string.
* The point is that we allocate that string in chunks of 256 bytes.
*/
typedef struct {
char* output;
size_t free_space;
size_t string_len;
size_t allocated;
} json_writer_userdata;
/* This function checks if there's enough space left in the output buffer,
* and will enlarge it if necessary. We're only allocating chunks of 256
* bytes at a time (or multiples thereof).
*/
static void json_writer_output_check(void* userdata, size_t needed) {
json_writer_userdata* state = userdata;
if (state->free_space >= needed) return;
needed -= state->free_space;
/* Round up by 256 bytes. */
needed = (needed + 0xff) & ~0xff;
state->output = gpr_realloc(state->output, state->allocated + needed);
state->free_space += needed;
state->allocated += needed;
}
/* These are needed by the writer's implementation. */
static void json_writer_output_char(void* userdata, char c) {
json_writer_userdata* state = userdata;
json_writer_output_check(userdata, 1);
state->output[state->string_len++] = c;
state->free_space--;
}
static void json_writer_output_string_with_len(void* userdata,
const char* str, size_t len) {
json_writer_userdata* state = userdata;
json_writer_output_check(userdata, len);
memcpy(state->output + state->string_len, str, len);
state->string_len += len;
state->free_space -= len;
}
static void json_writer_output_string(void* userdata,
const char* str) {
size_t len = strlen(str);
json_writer_output_string_with_len(userdata, str, len);
}
/* The reader asks us to clear our scratchpad. In our case, we'll simply mark
* the end of the current string, and advance our output pointer.
*/
static void json_reader_string_clear(void* userdata) {
json_reader_userdata* state = userdata;
if (state->string) {
GPR_ASSERT(state->string_ptr < state->input);
*state->string_ptr++ = 0;
}
state->string = state->string_ptr;
}
static void json_reader_string_add_char(void* userdata, gpr_uint32 c) {
json_reader_userdata* state = userdata;
GPR_ASSERT(state->string_ptr < state->input);
GPR_ASSERT(c <= 0xff);
*state->string_ptr++ = (char)c;
}
/* We are converting a UTF-32 character into UTF-8 here,
* as described by RFC3629.
*/
static void json_reader_string_add_utf32(void* userdata, gpr_uint32 c) {
if (c <= 0x7f) {
json_reader_string_add_char(userdata, c);
} else if (c <= 0x7ff) {
int b1 = 0xc0 | ((c >> 6) & 0x1f);
int b2 = 0x80 | (c & 0x3f);
json_reader_string_add_char(userdata, b1);
json_reader_string_add_char(userdata, b2);
} else if (c <= 0xffff) {
int b1 = 0xe0 | ((c >> 12) & 0x0f);
int b2 = 0x80 | ((c >> 6) & 0x3f);
int b3 = 0x80 | (c & 0x3f);
json_reader_string_add_char(userdata, b1);
json_reader_string_add_char(userdata, b2);
json_reader_string_add_char(userdata, b3);
} else if (c <= 0x1fffff) {
int b1 = 0xf0 | ((c >> 18) & 0x07);
int b2 = 0x80 | ((c >> 12) & 0x3f);
int b3 = 0x80 | ((c >> 6) & 0x3f);
int b4 = 0x80 | (c & 0x3f);
json_reader_string_add_char(userdata, b1);
json_reader_string_add_char(userdata, b2);
json_reader_string_add_char(userdata, b3);
json_reader_string_add_char(userdata, b4);
}
}
/* We consider that the input may be a zero-terminated string. So we
* can end up hitting eof before the end of the alleged string length.
*/
static gpr_uint32 json_reader_read_char(void* userdata) {
gpr_uint32 r;
json_reader_userdata* state = userdata;
if (state->remaining_input == 0) return GRPC_JSON_READ_CHAR_EOF;
r = *state->input++;
state->remaining_input--;
if (r == 0) {
state->remaining_input = 0;
return GRPC_JSON_READ_CHAR_EOF;
}
return r;
}
/* Helper function to create a new grpc_json object and link it into
* our tree-in-progress inside our opaque structure.
*/
static grpc_json* json_create_and_link(void* userdata,
grpc_json_type type) {
json_reader_userdata* state = userdata;
grpc_json* json = grpc_json_create(type);
json->parent = state->current_container;
json->prev = state->current_value;
state->current_value = json;
if (json->prev) {
json->prev->next = json;
}
if (json->parent) {
if (!json->parent->child) {
json->parent->child = json;
}
if (json->parent->type == GRPC_JSON_OBJECT) {
json->key = (char*) state->key;
}
}
if (!state->top) {
state->top = json;
}
return json;
}
static void json_reader_container_begins(void* userdata, grpc_json_type type) {
json_reader_userdata* state = userdata;
grpc_json* container;
GPR_ASSERT(type == GRPC_JSON_ARRAY || type == GRPC_JSON_OBJECT);
container = json_create_and_link(userdata, type);
state->current_container = container;
state->current_value = NULL;
}
/* It's important to remember that the reader is mostly stateless, so it
* isn't trying to remember what the container was prior the one that just
* ends. Since we're keeping track of these for our own purpose, we are
* able to return that information back, which is useful for it to validate
* the input json stream.
*
* Also note that if we're at the top of the tree, and the last container
* ends, we have to return GRPC_JSON_TOP_LEVEL.
*/
static grpc_json_type json_reader_container_ends(void* userdata) {
grpc_json_type container_type = GRPC_JSON_TOP_LEVEL;
json_reader_userdata* state = userdata;
GPR_ASSERT(state->current_container);
state->current_value = state->current_container;
state->current_container = state->current_container->parent;
if (state->current_container) {
container_type = state->current_container->type;
}
return container_type;
}
/* The next 3 functions basically are the reader asking us to use our string
* scratchpad for one of these 3 purposes.
*
* Note that in the set_number case, we're not going to try interpreting it.
* We'll keep it as a string, and leave it to the caller to evaluate it.
*/
static void json_reader_set_key(void* userdata) {
json_reader_userdata* state = userdata;
state->key = state->string;
}
static void json_reader_set_string(void* userdata) {
json_reader_userdata* state = userdata;
grpc_json* json = json_create_and_link(userdata, GRPC_JSON_STRING);
json->value = (char*) state->string;
}
static int json_reader_set_number(void* userdata) {
json_reader_userdata* state = userdata;
grpc_json* json = json_create_and_link(userdata, GRPC_JSON_NUMBER);
json->value = (char*) state->string;
return 1;
}
/* The object types true, false and null are self-sufficient, and don't need
* any more information beside their type.
*/
static void json_reader_set_true(void* userdata) {
json_create_and_link(userdata, GRPC_JSON_TRUE);
}
static void json_reader_set_false(void* userdata) {
json_create_and_link(userdata, GRPC_JSON_FALSE);
}
static void json_reader_set_null(void* userdata) {
json_create_and_link(userdata, GRPC_JSON_NULL);
}
static grpc_json_reader_vtable reader_vtable = {
json_reader_string_clear,
json_reader_string_add_char,
json_reader_string_add_utf32,
json_reader_read_char,
json_reader_container_begins,
json_reader_container_ends,
json_reader_set_key,
json_reader_set_string,
json_reader_set_number,
json_reader_set_true,
json_reader_set_false,
json_reader_set_null
};
/* And finally, let's define our public API. */
grpc_json* grpc_json_parse_string_with_len(char* input, size_t size) {
grpc_json_reader reader;
json_reader_userdata state;
grpc_json *json = NULL;
grpc_json_reader_status status;
if (!input) return NULL;
state.top = state.current_container = state.current_value = NULL;
state.string = state.key = NULL;
state.string_ptr = state.input = (gpr_uint8*) input;
state.remaining_input = size;
grpc_json_reader_init(&reader, &reader_vtable, &state);
status = grpc_json_reader_run(&reader);
json = state.top;
if ((status != GRPC_JSON_DONE) && json) {
grpc_json_destroy(json);
json = NULL;
}
return json;
}
#define UNBOUND_JSON_STRING_LENGTH 0x7fffffff
grpc_json* grpc_json_parse_string(char* input) {
return grpc_json_parse_string_with_len(input, UNBOUND_JSON_STRING_LENGTH);
}
static void json_dump_recursive(grpc_json_writer* writer,
grpc_json* json, int in_object) {
while (json) {
if (in_object) grpc_json_writer_object_key(writer, json->key);
switch (json->type) {
case GRPC_JSON_OBJECT:
case GRPC_JSON_ARRAY:
grpc_json_writer_container_begins(writer, json->type);
if (json->child)
json_dump_recursive(writer, json->child,
json->type == GRPC_JSON_OBJECT);
grpc_json_writer_container_ends(writer, json->type);
break;
case GRPC_JSON_STRING:
grpc_json_writer_value_string(writer, json->value);
break;
case GRPC_JSON_NUMBER:
grpc_json_writer_value_raw(writer, json->value);
break;
case GRPC_JSON_TRUE:
grpc_json_writer_value_raw_with_len(writer, "true", 4);
break;
case GRPC_JSON_FALSE:
grpc_json_writer_value_raw_with_len(writer, "false", 5);
break;
case GRPC_JSON_NULL:
grpc_json_writer_value_raw_with_len(writer, "null", 4);
break;
default:
abort();
}
json = json->next;
}
}
static grpc_json_writer_vtable writer_vtable = {
json_writer_output_char,
json_writer_output_string,
json_writer_output_string_with_len
};
char* grpc_json_dump_to_string(grpc_json* json, int indent) {
grpc_json_writer writer;
json_writer_userdata state;
state.output = NULL;
state.free_space = state.string_len = state.allocated = 0;
grpc_json_writer_init(&writer, indent, &writer_vtable, &state);
json_dump_recursive(&writer, json, 0);
json_writer_output_char(&state, 0);
return state.output;
}

@ -0,0 +1,252 @@
/*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <string.h>
#include <grpc/support/port_platform.h>
#include "src/core/json/json_writer.h"
static void json_writer_output_char(grpc_json_writer* writer, char c) {
writer->vtable->output_char(writer->userdata, c);
}
static void json_writer_output_string(grpc_json_writer* writer, const char* str) {
writer->vtable->output_string(writer->userdata, str);
}
static void json_writer_output_string_with_len(grpc_json_writer* writer, const char* str, size_t len) {
writer->vtable->output_string_with_len(writer->userdata, str, len);
}
void grpc_json_writer_init(grpc_json_writer* writer, int indent,
grpc_json_writer_vtable* vtable, void* userdata) {
memset(writer, 0, sizeof(grpc_json_writer));
writer->container_empty = 1;
writer->indent = indent;
writer->vtable = vtable;
writer->userdata = userdata;
}
static void json_writer_output_indent(
grpc_json_writer* writer) {
static const char spacesstr[] =
" "
" "
" "
" ";
unsigned spaces = writer->depth * writer->indent;
if (writer->indent == 0) return;
if (writer->got_key) {
json_writer_output_char(writer, ' ');
return;
}
while (spaces >= (sizeof(spacesstr) - 1)) {
json_writer_output_string_with_len(writer, spacesstr,
sizeof(spacesstr) - 1);
spaces -= (sizeof(spacesstr) - 1);
}
if (spaces == 0) return;
json_writer_output_string_with_len(
writer, spacesstr + sizeof(spacesstr) - 1 - spaces, spaces);
}
static void json_writer_value_end(grpc_json_writer* writer) {
if (writer->container_empty) {
writer->container_empty = 0;
if ((writer->indent == 0) || (writer->depth == 0)) return;
json_writer_output_char(writer, '\n');
} else {
json_writer_output_char(writer, ',');
if (writer->indent == 0) return;
json_writer_output_char(writer, '\n');
}
}
static void json_writer_escape_utf16(grpc_json_writer* writer, gpr_uint16 utf16) {
static const char hex[] = "0123456789abcdef";
json_writer_output_string_with_len(writer, "\\u", 2);
json_writer_output_char(writer, hex[(utf16 >> 12) & 0x0f]);
json_writer_output_char(writer, hex[(utf16 >> 8) & 0x0f]);
json_writer_output_char(writer, hex[(utf16 >> 4) & 0x0f]);
json_writer_output_char(writer, hex[(utf16) & 0x0f]);
}
static void json_writer_escape_string(grpc_json_writer* writer,
const char* string) {
json_writer_output_char(writer, '"');
for (;;) {
gpr_uint8 c = (gpr_uint8)*string++;
if (c == 0) {
break;
} else if ((c >= 32) && (c <= 127)) {
if ((c == '\\') || (c == '"')) json_writer_output_char(writer, '\\');
json_writer_output_char(writer, c);
} else if (c < 32) {
switch (c) {
case '\b':
json_writer_output_string_with_len(writer, "\\b", 2);
break;
case '\f':
json_writer_output_string_with_len(writer, "\\f", 2);
break;
case '\n':
json_writer_output_string_with_len(writer, "\\n", 2);
break;
case '\r':
json_writer_output_string_with_len(writer, "\\r", 2);
break;
case '\t':
json_writer_output_string_with_len(writer, "\\t", 2);
break;
default:
json_writer_escape_utf16(writer, c);
break;
}
} else {
gpr_uint32 utf32 = 0;
int extra = 0;
int i;
int valid = 1;
if ((c & 0xe0) == 0xc0) {
utf32 = c & 0x1f;
extra = 1;
} else if ((c & 0xf0) == 0xe0) {
utf32 = c & 0x0f;
extra = 2;
} else if ((c & 0xf8) == 0xf0) {
utf32 = c & 0x07;
extra = 3;
} else {
break;
}
for (i = 0; i < extra; i++) {
utf32 <<= 6;
c = *string++;
if ((c & 0xc0) != 0x80) {
valid = 0;
break;
}
utf32 |= c & 0x3f;
}
if (!valid) break;
/* The range 0xd800 - 0xdfff is reserved by the surrogates ad vitam.
* Any other range is technically reserved for future usage, so if we
* don't want the software to break in the future, we have to allow
* anything else. The first non-unicode character is 0x110000. */
if (((utf32 >= 0xd800) && (utf32 <= 0xdfff)) ||
(utf32 >= 0x110000)) break;
if (utf32 >= 0x10000) {
/* If utf32 contains a character that is above 0xffff, it needs to be
* broken down into a utf-16 surrogate pair. A surrogate pair is first
* a high surrogate, followed by a low surrogate. Each surrogate holds
* 10 bits of usable data, thus allowing a total of 20 bits of data.
* The high surrogate marker is 0xd800, while the low surrogate marker
* is 0xdc00. The low 10 bits of each will be the usable data.
*
* After re-combining the 20 bits of data, one has to add 0x10000 to
* the resulting value, in order to obtain the original character.
* This is obviously because the range 0x0000 - 0xffff can be written
* without any special trick.
*
* Since 0x10ffff is the highest allowed character, we're working in
* the range 0x00000 - 0xfffff after we decrement it by 0x10000.
* That range is exactly 20 bits.
*/
utf32 -= 0x10000;
json_writer_escape_utf16(writer, 0xd800 | (utf32 >> 10));
json_writer_escape_utf16(writer, 0xdc00 | (utf32 & 0x3ff));
} else {
json_writer_escape_utf16(writer, utf32);
}
}
}
json_writer_output_char(writer, '"');
}
void grpc_json_writer_container_begins(grpc_json_writer* writer, grpc_json_type type) {
if (!writer->got_key) json_writer_value_end(writer);
json_writer_output_indent(writer);
json_writer_output_char(writer, type == GRPC_JSON_OBJECT ? '{' : '[');
writer->container_empty = 1;
writer->got_key = 0;
writer->depth++;
}
void grpc_json_writer_container_ends(grpc_json_writer* writer, grpc_json_type type) {
if (writer->indent && !writer->container_empty)
json_writer_output_char(writer, '\n');
writer->depth--;
if (!writer->container_empty) json_writer_output_indent(writer);
json_writer_output_char(writer, type == GRPC_JSON_OBJECT ? '}' : ']');
writer->container_empty = 0;
writer->got_key = 0;
}
void grpc_json_writer_object_key(grpc_json_writer* writer, const char* string) {
json_writer_value_end(writer);
json_writer_output_indent(writer);
json_writer_escape_string(writer, string);
json_writer_output_char(writer, ':');
writer->got_key = 1;
}
void grpc_json_writer_value_raw(grpc_json_writer* writer, const char* string) {
if (!writer->got_key) json_writer_value_end(writer);
json_writer_output_indent(writer);
json_writer_output_string(writer, string);
writer->got_key = 0;
}
void grpc_json_writer_value_raw_with_len(grpc_json_writer* writer, const char* string, size_t len) {
if (!writer->got_key) json_writer_value_end(writer);
json_writer_output_indent(writer);
json_writer_output_string_with_len(writer, string, len);
writer->got_key = 0;
}
void grpc_json_writer_value_string(grpc_json_writer* writer, const char* string) {
if (!writer->got_key) json_writer_value_end(writer);
json_writer_output_indent(writer);
json_writer_escape_string(writer, string);
writer->got_key = 0;
}

@ -0,0 +1,93 @@
/*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/* The idea of the writer is basically symmetrical of the reader. While the
* reader emits various calls to your code, the writer takes basically the
* same calls and emit json out of it. It doesn't try to make any check on
* the order of the calls you do on it. Meaning you can theorically force
* it to generate invalid json.
*
* Also, unlike the reader, the writer expects UTF-8 encoded input strings.
* These strings will be UTF-8 validated, and any invalid character will
* cut the conversion short, before any invalid UTF-8 sequence, thus forming
* a valid UTF-8 string overall.
*/
#ifndef __GRPC_SRC_CORE_JSON_JSON_WRITER_H__
#define __GRPC_SRC_CORE_JSON_JSON_WRITER_H__
#include <stdlib.h>
#include "src/core/json/json_common.h"
typedef struct grpc_json_writer_vtable {
/* Adds a character to the output stream. */
void (*output_char)(void* userdata, char);
/* Adds a zero-terminated string to the output stream. */
void (*output_string)(void* userdata, const char* str);
/* Adds a fixed-length string to the output stream. */
void (*output_string_with_len)(void* userdata, const char* str, size_t len);
} grpc_json_writer_vtable;
typedef struct grpc_json_writer {
void* userdata;
grpc_json_writer_vtable* vtable;
int indent;
int depth;
int container_empty;
int got_key;
} grpc_json_writer;
/* Call this to initialize your writer structure. The indent parameter is
* specifying the number of spaces to use for indenting the output. If you
* use indent=0, then the output will not have any newlines either, thus
* emitting a condensed json output.
*/
void grpc_json_writer_init(grpc_json_writer* writer, int indent,
grpc_json_writer_vtable* vtable, void* userdata);
/* Signals the beginning of a container. */
void grpc_json_writer_container_begins(grpc_json_writer* writer, grpc_json_type type);
/* Signals the end of a container. */
void grpc_json_writer_container_ends(grpc_json_writer* writer, grpc_json_type type);
/* Writes down an object key for the next value. */
void grpc_json_writer_object_key(grpc_json_writer* writer, const char* string);
/* Sets a raw value. Useful for numbers. */
void grpc_json_writer_value_raw(grpc_json_writer* writer, const char* string);
/* Sets a raw value with its length. Useful for values like true or false. */
void grpc_json_writer_value_raw_with_len(grpc_json_writer* writer, const char* string, size_t len);
/* Sets a string value. It'll be escaped, and utf-8 validated. */
void grpc_json_writer_value_string(grpc_json_writer* writer, const char* string);
#endif /* __GRPC_SRC_CORE_JSON_JSON_WRITER_H__ */

@ -35,22 +35,49 @@
#include <string.h>
#include "src/core/security/security_context.h"
#include "src/core/security/credentials.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include "src/core/support/string.h"
#include "src/core/channel/channel_stack.h"
#include "src/core/security/security_context.h"
#include "src/core/security/credentials.h"
#include "src/core/surface/call.h"
/* We can have a per-call credentials. */
typedef struct {
grpc_credentials *creds;
grpc_mdstr *host;
grpc_call_op op;
} call_data;
/* We can have a per-channel credentials. */
typedef struct {
grpc_channel_security_context *security_context;
grpc_mdctx *md_ctx;
grpc_mdstr *authority_string;
grpc_mdstr *error_msg_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;
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, 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);
grpc_call_element_send_cancel(elem);
}
static void on_credentials_metadata(void *user_data, grpc_mdelem **md_elems,
size_t num_md,
grpc_credentials_status status) {
@ -62,6 +89,46 @@ static void on_credentials_metadata(void *user_data, grpc_mdelem **md_elems,
grpc_call_next_op(elem, &((call_data *)elem->call_data)->op);
}
static void send_security_metadata(grpc_call_element *elem, grpc_call_op *op) {
/* grab pointers to our data from the call element */
call_data *calld = elem->call_data;
channel_data *channeld = elem->channel_data;
grpc_credentials *channel_creds =
channeld->security_context->request_metadata_creds;
/* TODO(jboeuf):
Decide on the policy in this case:
- populate both channel and call?
- the call takes precedence over the channel?
- leave this decision up to the channel credentials? */
if (calld->creds != NULL) {
gpr_log(GPR_ERROR, "Ignoring per call credentials for now.");
}
if (channel_creds != NULL &&
grpc_credentials_has_request_metadata(channel_creds)) {
calld->op = *op; /* Copy op (originates from the caller's stack). */
grpc_credentials_get_request_metadata(channel_creds,
on_credentials_metadata, elem);
} else {
grpc_call_next_op(elem, op);
}
}
static void on_host_checked(void *user_data, grpc_security_status status) {
grpc_call_element *elem = (grpc_call_element *)user_data;
call_data *calld = elem->call_data;
if (status == GRPC_SECURITY_OK) {
send_security_metadata(elem, &calld->op);
} else {
char *error_msg;
gpr_asprintf(&error_msg, "Invalid host %s set in :authority metadata.",
grpc_mdstr_as_c_string(calld->host));
bubbleup_error(elem, error_msg);
gpr_free(error_msg);
}
}
/* Called either:
- in response to an API call (or similar) from above, to send something
- a network event (or similar) from below, to receive something
@ -74,26 +141,36 @@ static void call_op(grpc_call_element *elem, grpc_call_element *from_elem,
channel_data *channeld = elem->channel_data;
switch (op->type) {
case GRPC_SEND_START: {
grpc_credentials *channel_creds =
channeld->security_context->request_metadata_creds;
/* TODO(jboeuf):
Decide on the policy in this case:
- populate both channel and call?
- the call takes precedence over the channel?
- leave this decision up to the channel credentials? */
if (calld->creds != NULL) {
gpr_log(GPR_ERROR, "Ignoring per call credentials for now.");
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);
}
if (channel_creds != NULL &&
grpc_credentials_has_request_metadata(channel_creds)) {
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). */
grpc_credentials_get_request_metadata(channel_creds,
on_credentials_metadata, elem);
break;
status = grpc_channel_security_context_check_call_host(
channeld->security_context, call_host, on_host_checked, elem);
if (status != GRPC_SECURITY_OK) {
if (status == GRPC_SECURITY_ERROR) {
char *error_msg;
gpr_asprintf(&error_msg,
"Invalid host %s set in :authority metadata.",
call_host);
bubbleup_error(elem, error_msg);
gpr_free(error_msg);
}
break;
}
}
/* FALLTHROUGH INTENDED. */
}
send_security_metadata(elem, op);
break;
default:
/* pass control up or down the stack depending on op->dir */
@ -116,6 +193,7 @@ static void init_call_elem(grpc_call_element *elem,
Find a way to pass-in the credentials from the caller here. */
call_data *calld = elem->call_data;
calld->creds = NULL;
calld->host = NULL;
}
/* Destructor for call_data */
@ -124,6 +202,9 @@ static void destroy_call_elem(grpc_call_element *elem) {
if (calld->creds != NULL) {
grpc_credentials_unref(calld->creds);
}
if (calld->host != NULL) {
grpc_mdstr_unref(calld->host);
}
}
/* Constructor for channel_data */
@ -146,6 +227,11 @@ static void init_channel_elem(grpc_channel_element *elem,
GPR_ASSERT(ctx->is_client_side);
channeld->security_context =
(grpc_channel_security_context *)grpc_security_context_ref(ctx);
channeld->md_ctx = metadata_context;
channeld->authority_string =
grpc_mdstr_from_string(channeld->md_ctx, ":authority");
channeld->error_msg_key =
grpc_mdstr_from_string(channeld->md_ctx, "grpc-message");
}
/* Destructor for channel data */
@ -154,6 +240,12 @@ static void destroy_channel_elem(grpc_channel_element *elem) {
channel_data *channeld = elem->channel_data;
grpc_channel_security_context *ctx = channeld->security_context;
if (ctx != NULL) grpc_security_context_unref(&ctx->base);
if (channeld->authority_string != NULL) {
grpc_mdstr_unref(channeld->authority_string);
}
if (channeld->error_msg_key != NULL) {
grpc_mdstr_unref(channeld->error_msg_key);
}
}
const grpc_channel_filter grpc_client_auth_filter = {

@ -113,7 +113,8 @@ char *grpc_base64_encode(const void *vdata, size_t data_size, int url_safe,
*current++ = GRPC_BASE64_PAD_CHAR;
}
GPR_ASSERT((current - result) < result_projected_size);
GPR_ASSERT(current >= result);
GPR_ASSERT((gpr_uintptr)(current - result) < result_projected_size);
result[current - result] = '\0';
return result;
}

@ -42,7 +42,7 @@
#include <grpc/support/sync.h>
#include <grpc/support/time.h>
#include "third_party/cJSON/cJSON.h"
#include "src/core/json/json.h"
#include <string.h>
#include <stdio.h>
@ -173,7 +173,9 @@ static void ssl_server_destroy(grpc_server_credentials *creds) {
gpr_free(creds);
}
static int ssl_has_request_metadata(const grpc_credentials *creds) { return 0; }
static int ssl_has_request_metadata(const grpc_credentials *creds) {
return 0;
}
static int ssl_has_request_metadata_only(const grpc_credentials *creds) {
return 0;
@ -214,14 +216,10 @@ static void ssl_copy_key_material(const char *input, unsigned char **output,
static void ssl_build_config(const char *pem_root_certs,
grpc_ssl_pem_key_cert_pair *pem_key_cert_pair,
grpc_ssl_config *config) {
if (pem_root_certs == NULL) {
/* TODO(jboeuf): Get them from the environment. */
gpr_log(GPR_ERROR, "Default SSL roots not yet implemented.");
} else {
if (pem_root_certs != NULL) {
ssl_copy_key_material(pem_root_certs, &config->pem_root_certs,
&config->pem_root_certs_size);
}
if (pem_key_cert_pair != NULL) {
GPR_ASSERT(pem_key_cert_pair->private_key != NULL);
GPR_ASSERT(pem_key_cert_pair->cert_chain != NULL);
@ -336,7 +334,7 @@ grpc_oauth2_token_fetcher_credentials_parse_server_response(
char *null_terminated_body = NULL;
char *new_access_token = NULL;
grpc_credentials_status status = GRPC_CREDENTIALS_OK;
cJSON *json = NULL;
grpc_json *json = NULL;
if (response->body_length > 0) {
null_terminated_body = gpr_malloc(response->body_length + 1);
@ -351,41 +349,48 @@ grpc_oauth2_token_fetcher_credentials_parse_server_response(
status = GRPC_CREDENTIALS_ERROR;
goto end;
} else {
cJSON *access_token = NULL;
cJSON *token_type = NULL;
cJSON *expires_in = NULL;
json = cJSON_Parse(null_terminated_body);
grpc_json *access_token = NULL;
grpc_json *token_type = NULL;
grpc_json *expires_in = NULL;
grpc_json *ptr;
json = grpc_json_parse_string(null_terminated_body);
if (json == NULL) {
gpr_log(GPR_ERROR, "Could not parse JSON from %s", null_terminated_body);
status = GRPC_CREDENTIALS_ERROR;
goto end;
}
if (json->type != cJSON_Object) {
if (json->type != GRPC_JSON_OBJECT) {
gpr_log(GPR_ERROR, "Response should be a JSON object");
status = GRPC_CREDENTIALS_ERROR;
goto end;
}
access_token = cJSON_GetObjectItem(json, "access_token");
if (access_token == NULL || access_token->type != cJSON_String) {
for (ptr = json->child; ptr; ptr = ptr->next) {
if (strcmp(ptr->key, "access_token") == 0) {
access_token = ptr;
} else if (strcmp(ptr->key, "token_type") == 0) {
token_type = ptr;
} else if (strcmp(ptr->key, "expires_in") == 0) {
expires_in = ptr;
}
}
if (access_token == NULL || access_token->type != GRPC_JSON_STRING) {
gpr_log(GPR_ERROR, "Missing or invalid access_token in JSON.");
status = GRPC_CREDENTIALS_ERROR;
goto end;
}
token_type = cJSON_GetObjectItem(json, "token_type");
if (token_type == NULL || token_type->type != cJSON_String) {
if (token_type == NULL || token_type->type != GRPC_JSON_STRING) {
gpr_log(GPR_ERROR, "Missing or invalid token_type in JSON.");
status = GRPC_CREDENTIALS_ERROR;
goto end;
}
expires_in = cJSON_GetObjectItem(json, "expires_in");
if (expires_in == NULL || expires_in->type != cJSON_Number) {
if (expires_in == NULL || expires_in->type != GRPC_JSON_NUMBER) {
gpr_log(GPR_ERROR, "Missing or invalid expires_in in JSON.");
status = GRPC_CREDENTIALS_ERROR;
goto end;
}
gpr_asprintf(&new_access_token, "%s %s", token_type->valuestring,
access_token->valuestring);
token_lifetime->tv_sec = expires_in->valueint;
gpr_asprintf(&new_access_token, "%s %s", token_type->value,
access_token->value);
token_lifetime->tv_sec = strtol(expires_in->value, NULL, 10);
token_lifetime->tv_nsec = 0;
if (*token_elem != NULL) grpc_mdelem_unref(*token_elem);
*token_elem = grpc_mdelem_from_strings(ctx, GRPC_AUTHORIZATION_METADATA_KEY,
@ -400,7 +405,7 @@ end:
}
if (null_terminated_body != NULL) gpr_free(null_terminated_body);
if (new_access_token != NULL) gpr_free(new_access_token);
if (json != NULL) cJSON_Delete(json);
if (json != NULL) grpc_json_destroy(json);
return status;
}
@ -896,7 +901,9 @@ static void iam_destroy(grpc_credentials *creds) {
gpr_free(c);
}
static int iam_has_request_metadata(const grpc_credentials *creds) { return 1; }
static int iam_has_request_metadata(const grpc_credentials *creds) {
return 1;
}
static int iam_has_request_metadata_only(const grpc_credentials *creds) {
return 1;

@ -44,7 +44,8 @@
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include "third_party/cJSON/cJSON.h"
#include "src/core/json/json.h"
/* --- Constants. --- */
@ -64,18 +65,20 @@ static grpc_jwt_encode_and_sign_override g_jwt_encode_and_sign_override = NULL;
/* --- grpc_auth_json_key. --- */
static const char *json_get_string_property(cJSON *json,
static const char *json_get_string_property(grpc_json *json,
const char *prop_name) {
cJSON *child = NULL;
child = cJSON_GetObjectItem(json, prop_name);
if (child == NULL || child->type != cJSON_String) {
grpc_json *child;
for (child = json->child; child != NULL; child = child->next) {
if (strcmp(child->key, prop_name) == 0) break;
}
if (child == NULL || child->type != GRPC_JSON_STRING) {
gpr_log(GPR_ERROR, "Invalid or missing %s property.", prop_name);
return NULL;
}
return child->valuestring;
return child->value;
}
static int set_json_key_string_property(cJSON *json, const char *prop_name,
static int set_json_key_string_property(grpc_json *json, const char *prop_name,
char **json_key_field) {
const char *prop_value = json_get_string_property(json, prop_name);
if (prop_value == NULL) return 0;
@ -91,7 +94,8 @@ int grpc_auth_json_key_is_valid(const grpc_auth_json_key *json_key) {
grpc_auth_json_key grpc_auth_json_key_create_from_string(
const char *json_string) {
grpc_auth_json_key result;
cJSON *json = cJSON_Parse(json_string);
char *scratchpad = gpr_strdup(json_string);
grpc_json *json = grpc_json_parse_string(scratchpad);
BIO *bio = NULL;
const char *prop_value;
int success = 0;
@ -100,7 +104,7 @@ grpc_auth_json_key grpc_auth_json_key_create_from_string(
result.type = GRPC_AUTH_JSON_KEY_TYPE_INVALID;
if (json == NULL) {
gpr_log(GPR_ERROR, "Invalid json string %s", json_string);
return result;
goto end;
}
prop_value = json_get_string_property(json, "type");
@ -123,7 +127,8 @@ grpc_auth_json_key grpc_auth_json_key_create_from_string(
goto end;
}
bio = BIO_new(BIO_s_mem());
if (BIO_puts(bio, prop_value) != strlen(prop_value)) {
success = BIO_puts(bio, prop_value);
if ((success < 0) || ((size_t)success != strlen(prop_value))) {
gpr_log(GPR_ERROR, "Could not write into openssl BIO.");
goto end;
}
@ -136,8 +141,9 @@ grpc_auth_json_key grpc_auth_json_key_create_from_string(
end:
if (bio != NULL) BIO_free(bio);
if (json != NULL) cJSON_Delete(json);
if (json != NULL) grpc_json_destroy(json);
if (!success) grpc_auth_json_key_destruct(&result);
gpr_free(scratchpad);
return result;
}
@ -164,49 +170,63 @@ void grpc_auth_json_key_destruct(grpc_auth_json_key *json_key) {
/* --- jwt encoding and signature. --- */
static grpc_json *create_child(grpc_json *brother, grpc_json *parent,
const char *key, const char *value,
grpc_json_type type) {
grpc_json *child = grpc_json_create(type);
if (brother) brother->next = child;
if (!parent->child) parent->child = child;
child->parent = parent;
child->value = value;
child->key = key;
return child;
}
static char *encoded_jwt_header(const char *algorithm) {
cJSON *json = cJSON_CreateObject();
cJSON *child = cJSON_CreateString(algorithm);
grpc_json *json = grpc_json_create(GRPC_JSON_OBJECT);
grpc_json *child = NULL;
char *json_str = NULL;
char *result = NULL;
cJSON_AddItemToObject(json, "alg", child);
child = cJSON_CreateString(GRPC_JWT_TYPE);
cJSON_AddItemToObject(json, "typ", child);
json_str = cJSON_PrintUnformatted(json);
child = create_child(NULL, json, "alg", algorithm, GRPC_JSON_STRING);
create_child(child, json, "typ", GRPC_JWT_TYPE, GRPC_JSON_STRING);
json_str = grpc_json_dump_to_string(json, 0);
result = grpc_base64_encode(json_str, strlen(json_str), 1, 0);
free(json_str);
cJSON_Delete(json);
gpr_free(json_str);
grpc_json_destroy(json);
return result;
}
static char *encoded_jwt_claim(const grpc_auth_json_key *json_key,
const char *scope, gpr_timespec token_lifetime) {
cJSON *json = cJSON_CreateObject();
cJSON *child = NULL;
grpc_json *json = grpc_json_create(GRPC_JSON_OBJECT);
grpc_json *child = NULL;
char *json_str = NULL;
char *result = NULL;
gpr_timespec now = gpr_now();
gpr_timespec expiration = gpr_time_add(now, token_lifetime);
/* log10(2^64) ~= 20 */
char now_str[24];
char expiration_str[24];
if (gpr_time_cmp(token_lifetime, grpc_max_auth_token_lifetime) > 0) {
gpr_log(GPR_INFO, "Cropping token lifetime to maximum allowed value.");
expiration = gpr_time_add(now, grpc_max_auth_token_lifetime);
}
child = cJSON_CreateString(json_key->client_email);
cJSON_AddItemToObject(json, "iss", child);
child = cJSON_CreateString(scope);
cJSON_AddItemToObject(json, "scope", child);
child = cJSON_CreateString(GRPC_JWT_AUDIENCE);
cJSON_AddItemToObject(json, "aud", child);
child = cJSON_CreateNumber(now.tv_sec);
cJSON_SetIntValue(child, now.tv_sec);
cJSON_AddItemToObject(json, "iat", child);
child = cJSON_CreateNumber(expiration.tv_sec);
cJSON_SetIntValue(child, expiration.tv_sec);
cJSON_AddItemToObject(json, "exp", child);
json_str = cJSON_PrintUnformatted(json);
sprintf(now_str, "%ld", now.tv_sec);
sprintf(expiration_str, "%ld", expiration.tv_sec);
child = create_child(NULL, json, "iss", json_key->client_email,
GRPC_JSON_STRING);
child = create_child(child, json, "scope", scope, GRPC_JSON_STRING);
child = create_child(child, json, "aud", GRPC_JWT_AUDIENCE, GRPC_JSON_STRING);
child = create_child(child, json, "iat", now_str, GRPC_JSON_NUMBER);
create_child(child, json, "exp", expiration_str, GRPC_JSON_NUMBER);
json_str = grpc_json_dump_to_string(json, 0);
result = grpc_base64_encode(json_str, strlen(json_str), 1, 0);
free(json_str);
cJSON_Delete(json);
gpr_free(json_str);
grpc_json_destroy(json);
return result;
}
@ -216,12 +236,13 @@ static char *dot_concat_and_free_strings(char *str1, char *str2) {
size_t result_len = str1_len + 1 /* dot */ + str2_len;
char *result = gpr_malloc(result_len + 1 /* NULL terminated */);
char *current = result;
strncpy(current, str1, str1_len);
memcpy(current, str1, str1_len);
current += str1_len;
*(current++) = '.';
strncpy(current, str2, str2_len);
memcpy(current, str2, str2_len);
current += str2_len;
GPR_ASSERT((current - result) == result_len);
GPR_ASSERT(current >= result);
GPR_ASSERT((gpr_uintptr)(current - result) == result_len);
*current = '\0';
gpr_free(str1);
gpr_free(str2);

@ -111,7 +111,7 @@ static void call_read_cb(secure_endpoint *ep, gpr_slice *slices, size_t nslices,
static void on_read(void *user_data, gpr_slice *slices, size_t nslices,
grpc_endpoint_cb_status error) {
int i = 0;
unsigned i;
gpr_uint8 keep_looping = 0;
int input_buffer_count = 0;
tsi_result result = TSI_OK;
@ -221,7 +221,7 @@ static grpc_endpoint_write_status endpoint_write(grpc_endpoint *secure_ep,
size_t nslices,
grpc_endpoint_write_cb cb,
void *user_data) {
int i = 0;
unsigned i;
int output_buffer_count = 0;
tsi_result result = TSI_OK;
secure_endpoint *ep = (secure_endpoint *)secure_ep;

@ -113,8 +113,7 @@ static void check_peer(grpc_secure_transport_setup *s) {
return;
}
peer_status =
grpc_security_context_check_peer(s->ctx, &peer, on_peer_checked, s);
tsi_peer_destruct(&peer);
grpc_security_context_check_peer(s->ctx, peer, on_peer_checked, s);
if (peer_status == GRPC_SECURITY_ERROR) {
gpr_log(GPR_ERROR, "Peer check failed.");
secure_transport_setup_done(s, 0);

@ -39,6 +39,8 @@
#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"
#include "src/core/support/file.h"
#include "src/core/support/string.h"
#include "src/core/surface/lame_client.h"
#include "src/core/transport/chttp2/alpn.h"
@ -67,12 +69,22 @@ grpc_security_status grpc_security_context_create_handshaker(
}
grpc_security_status grpc_security_context_check_peer(
grpc_security_context *ctx, const tsi_peer *peer,
grpc_security_check_peer_cb cb, void *user_data) {
if (ctx == NULL) return GRPC_SECURITY_ERROR;
grpc_security_context *ctx, tsi_peer peer, grpc_security_check_cb cb,
void *user_data) {
if (ctx == NULL) {
tsi_peer_destruct(&peer);
return GRPC_SECURITY_ERROR;
}
return ctx->vtable->check_peer(ctx, peer, cb, user_data);
}
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) {
if (ctx == NULL || ctx->check_call_host == NULL) return GRPC_SECURITY_ERROR;
return ctx->check_call_host(ctx, 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);
@ -135,6 +147,11 @@ static int check_request_metadata_creds(grpc_credentials *creds) {
/* -- Fake implementation. -- */
typedef struct {
grpc_channel_security_context base;
int call_host_check_is_async;
} grpc_fake_channel_security_context;
static void fake_channel_destroy(grpc_security_context *ctx) {
grpc_channel_security_context *c = (grpc_channel_security_context *)ctx;
grpc_credentials_unref(c->request_metadata_creds);
@ -156,31 +173,51 @@ static grpc_security_status fake_server_create_handshaker(
}
static grpc_security_status fake_check_peer(grpc_security_context *ctx,
const tsi_peer *peer,
grpc_security_check_peer_cb cb,
tsi_peer peer,
grpc_security_check_cb cb,
void *user_data) {
const char *prop_name;
if (peer->property_count != 1) {
grpc_security_status status = GRPC_SECURITY_OK;
if (peer.property_count != 1) {
gpr_log(GPR_ERROR, "Fake peers should only have 1 property.");
return GRPC_SECURITY_ERROR;
status = GRPC_SECURITY_ERROR;
goto end;
}
prop_name = peer->properties[0].name;
prop_name = peer.properties[0].name;
if (prop_name == NULL ||
strcmp(prop_name, TSI_CERTIFICATE_TYPE_PEER_PROPERTY)) {
gpr_log(GPR_ERROR, "Unexpected property in fake peer: %s.",
prop_name == NULL ? "<EMPTY>" : prop_name);
return GRPC_SECURITY_ERROR;
status = GRPC_SECURITY_ERROR;
goto end;
}
if (peer->properties[0].type != TSI_PEER_PROPERTY_TYPE_STRING) {
if (peer.properties[0].type != TSI_PEER_PROPERTY_TYPE_STRING) {
gpr_log(GPR_ERROR, "Invalid type of cert type property.");
return GRPC_SECURITY_ERROR;
status = GRPC_SECURITY_ERROR;
goto end;
}
if (strncmp(peer->properties[0].value.string.data, TSI_FAKE_CERTIFICATE_TYPE,
peer->properties[0].value.string.length)) {
if (strncmp(peer.properties[0].value.string.data, TSI_FAKE_CERTIFICATE_TYPE,
peer.properties[0].value.string.length)) {
gpr_log(GPR_ERROR, "Invalid value for cert type property.");
return GRPC_SECURITY_ERROR;
status = GRPC_SECURITY_ERROR;
goto end;
}
end:
tsi_peer_destruct(&peer);
return status;
}
static grpc_security_status fake_channel_check_call_host(
grpc_channel_security_context *ctx, const char *host,
grpc_security_check_cb cb, void *user_data) {
grpc_fake_channel_security_context *c =
(grpc_fake_channel_security_context *)ctx;
if (c->call_host_check_is_async) {
cb(user_data, GRPC_SECURITY_OK);
return GRPC_SECURITY_PENDING;
} else {
return GRPC_SECURITY_OK;
}
return GRPC_SECURITY_OK;
}
static grpc_security_context_vtable fake_channel_vtable = {
@ -190,15 +227,17 @@ static grpc_security_context_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_credentials *request_metadata_creds) {
grpc_channel_security_context *c =
gpr_malloc(sizeof(grpc_channel_security_context));
gpr_ref_init(&c->base.refcount, 1);
c->base.is_client_side = 1;
c->base.vtable = &fake_channel_vtable;
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));
gpr_ref_init(&c->base.base.refcount, 1);
c->base.base.is_client_side = 1;
c->base.base.vtable = &fake_channel_vtable;
GPR_ASSERT(check_request_metadata_creds(request_metadata_creds));
c->request_metadata_creds = grpc_credentials_ref(request_metadata_creds);
return c;
c->base.request_metadata_creds = grpc_credentials_ref(request_metadata_creds);
c->base.check_call_host = fake_channel_check_call_host;
c->call_host_check_is_async = call_host_check_is_async;
return &c->base;
}
grpc_security_context *grpc_fake_server_security_context_create(void) {
@ -213,7 +252,9 @@ grpc_security_context *grpc_fake_server_security_context_create(void) {
typedef struct {
grpc_channel_security_context base;
tsi_ssl_handshaker_factory *handshaker_factory;
char *secure_peer_name;
char *target_name;
char *overridden_target_name;
tsi_peer peer;
} grpc_ssl_channel_security_context;
typedef struct {
@ -228,7 +269,9 @@ static void ssl_channel_destroy(grpc_security_context *ctx) {
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);
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);
}
@ -242,11 +285,11 @@ static void ssl_server_destroy(grpc_security_context *ctx) {
static grpc_security_status ssl_create_handshaker(
tsi_ssl_handshaker_factory *handshaker_factory, int is_client,
const char *secure_peer_name, tsi_handshaker **handshaker) {
const char *peer_name, tsi_handshaker **handshaker) {
tsi_result result = TSI_OK;
if (handshaker_factory == NULL) return GRPC_SECURITY_ERROR;
result = tsi_ssl_handshaker_factory_create_handshaker(
handshaker_factory, is_client ? secure_peer_name : NULL, handshaker);
handshaker_factory, is_client ? peer_name : NULL, handshaker);
if (result != TSI_OK) {
gpr_log(GPR_ERROR, "Handshaker creation failed with error %s.",
tsi_result_to_string(result));
@ -259,7 +302,10 @@ 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;
return ssl_create_handshaker(c->handshaker_factory, 1, c->secure_peer_name,
return ssl_create_handshaker(c->handshaker_factory, 1,
c->overridden_target_name != NULL
? c->overridden_target_name
: c->target_name,
handshaker);
}
@ -269,7 +315,7 @@ static grpc_security_status ssl_server_create_handshaker(
return ssl_create_handshaker(c->handshaker_factory, 0, NULL, handshaker);
}
static grpc_security_status ssl_check_peer(const char *secure_peer_name,
static grpc_security_status ssl_check_peer(const char *peer_name,
const tsi_peer *peer) {
/* Check the ALPN. */
const tsi_peer_property *p =
@ -289,28 +335,54 @@ static grpc_security_status ssl_check_peer(const char *secure_peer_name,
}
/* Check the peer name if specified. */
if (secure_peer_name != NULL &&
!tsi_ssl_peer_matches_name(peer, secure_peer_name)) {
gpr_log(GPR_ERROR, "Peer name %s is not in peer certificate",
secure_peer_name);
if (peer_name != NULL &&
!tsi_ssl_peer_matches_name(peer, peer_name)) {
gpr_log(GPR_ERROR, "Peer name %s is not in peer certificate", peer_name);
return GRPC_SECURITY_ERROR;
}
return GRPC_SECURITY_OK;
}
static grpc_security_status ssl_channel_check_peer(
grpc_security_context *ctx, const tsi_peer *peer,
grpc_security_check_peer_cb cb, void *user_data) {
static grpc_security_status ssl_channel_check_peer(grpc_security_context *ctx,
tsi_peer peer,
grpc_security_check_cb cb,
void *user_data) {
grpc_ssl_channel_security_context *c =
(grpc_ssl_channel_security_context *)ctx;
return ssl_check_peer(c->secure_peer_name, peer);
grpc_security_status status = ssl_check_peer(c->overridden_target_name != NULL
? c->overridden_target_name
: c->target_name,
&peer);
c->peer = peer;
return status;
}
static grpc_security_status ssl_server_check_peer(
grpc_security_context *ctx, const tsi_peer *peer,
grpc_security_check_peer_cb cb, void *user_data) {
static grpc_security_status ssl_server_check_peer(grpc_security_context *ctx,
tsi_peer peer,
grpc_security_check_cb cb,
void *user_data) {
/* TODO(jboeuf): Find a way to expose the peer to the authorization layer. */
return ssl_check_peer(NULL, peer);
grpc_security_status status = ssl_check_peer(NULL, &peer);
tsi_peer_destruct(&peer);
return status;
}
static grpc_security_status ssl_channel_check_call_host(
grpc_channel_security_context *ctx, const char *host,
grpc_security_check_cb cb, void *user_data) {
grpc_ssl_channel_security_context *c =
(grpc_ssl_channel_security_context *)ctx;
if (tsi_ssl_peer_matches_name(&c->peer, host)) return GRPC_SECURITY_OK;
/* If the target name was overridden, then the original target_name was
'checked' transitively during the previous peer check at the end of the
handshake. */
if (c->overridden_target_name != NULL && !strcmp(host, c->target_name)) {
return GRPC_SECURITY_OK;
} else {
return GRPC_SECURITY_ERROR;
}
}
static grpc_security_context_vtable ssl_channel_vtable = {
@ -319,9 +391,32 @@ static grpc_security_context_vtable ssl_channel_vtable = {
static grpc_security_context_vtable ssl_server_vtable = {
ssl_server_destroy, ssl_server_create_handshaker, ssl_server_check_peer};
static gpr_slice default_pem_root_certs;
static void init_default_pem_root_certs(void) {
char *default_root_certs_path =
gpr_getenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR);
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);
gpr_free(default_root_certs_path);
}
}
static size_t get_default_pem_roots(const unsigned char **pem_root_certs) {
/* TODO(jboeuf@google.com): Maybe revisit the approach which consists in
loading all the roots once for the lifetime of the process. */
static gpr_once once = GPR_ONCE_INIT;
gpr_once_init(&once, init_default_pem_root_certs);
*pem_root_certs = GPR_SLICE_START_PTR(default_pem_root_certs);
return GPR_SLICE_LENGTH(default_pem_root_certs);
}
grpc_security_status grpc_ssl_channel_security_context_create(
grpc_credentials *request_metadata_creds, const grpc_ssl_config *config,
const char *secure_peer_name, grpc_channel_security_context **ctx) {
const char *target_name, const char *overridden_target_name,
grpc_channel_security_context **ctx) {
size_t num_alpn_protocols = grpc_chttp2_num_alpn_versions();
const unsigned char **alpn_protocol_strings =
gpr_malloc(sizeof(const char *) * num_alpn_protocols);
@ -330,6 +425,8 @@ grpc_security_status grpc_ssl_channel_security_context_create(
tsi_result result = TSI_OK;
grpc_ssl_channel_security_context *c;
size_t i;
const unsigned char *pem_root_certs;
size_t pem_root_certs_size;
for (i = 0; i < num_alpn_protocols; i++) {
alpn_protocol_strings[i] =
@ -338,9 +435,8 @@ grpc_security_status grpc_ssl_channel_security_context_create(
strlen(grpc_chttp2_get_alpn_version_index(i));
}
if (config == NULL || secure_peer_name == NULL ||
config->pem_root_certs == NULL) {
gpr_log(GPR_ERROR, "An ssl channel needs a secure name and root certs.");
if (config == NULL || target_name == NULL) {
gpr_log(GPR_ERROR, "An ssl channel needs a config and a target name.");
goto error;
}
if (!check_request_metadata_creds(request_metadata_creds)) {
@ -354,14 +450,27 @@ grpc_security_status grpc_ssl_channel_security_context_create(
c->base.base.vtable = &ssl_channel_vtable;
c->base.base.is_client_side = 1;
c->base.request_metadata_creds = grpc_credentials_ref(request_metadata_creds);
if (secure_peer_name != NULL) {
c->secure_peer_name = gpr_strdup(secure_peer_name);
c->base.check_call_host = ssl_channel_check_call_host;
if (target_name != NULL) {
c->target_name = gpr_strdup(target_name);
}
if (overridden_target_name != NULL) {
c->overridden_target_name = gpr_strdup(overridden_target_name);
}
if (config->pem_root_certs == NULL) {
pem_root_certs_size = get_default_pem_roots(&pem_root_certs);
if (pem_root_certs == NULL || pem_root_certs_size == 0) {
gpr_log(GPR_ERROR, "Could not get default pem root certs.");
goto error;
}
} else {
pem_root_certs = config->pem_root_certs;
pem_root_certs_size = config->pem_root_certs_size;
}
result = tsi_create_ssl_client_handshaker_factory(
config->pem_private_key, config->pem_private_key_size,
config->pem_cert_chain, config->pem_cert_chain_size,
config->pem_root_certs, config->pem_root_certs_size,
GRPC_SSL_CIPHER_SUITES, alpn_protocol_strings,
config->pem_cert_chain, config->pem_cert_chain_size, pem_root_certs,
pem_root_certs_size, GRPC_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.",
@ -444,7 +553,7 @@ grpc_channel *grpc_ssl_channel_create(grpc_credentials *ssl_creds,
grpc_channel *channel = NULL;
grpc_security_status status = GRPC_SECURITY_OK;
size_t i = 0;
const char *secure_peer_name = target;
const char *overridden_target_name = NULL;
grpc_arg arg;
grpc_channel_args *new_args;
@ -452,13 +561,13 @@ grpc_channel *grpc_ssl_channel_create(grpc_credentials *ssl_creds,
grpc_arg *arg = &args->args[i];
if (!strcmp(arg->key, GRPC_SSL_TARGET_NAME_OVERRIDE_ARG) &&
arg->type == GRPC_ARG_STRING) {
secure_peer_name = arg->value.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),
secure_peer_name, &ctx);
target, overridden_target_name, &ctx);
if (status != GRPC_SECURITY_OK) {
return grpc_lame_client_channel_create();
}
@ -476,7 +585,7 @@ 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);
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);

@ -56,16 +56,15 @@ typedef struct grpc_security_context grpc_security_context;
#define GRPC_SECURITY_CONTEXT_ARG "grpc.security_context"
typedef void (*grpc_security_check_peer_cb)(void *user_data,
grpc_security_status status);
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,
const tsi_peer *peer,
grpc_security_check_peer_cb,
grpc_security_status (*check_peer)(grpc_security_context *ctx, tsi_peer peer,
grpc_security_check_cb cb,
void *user_data);
} grpc_security_context_vtable;
@ -87,18 +86,14 @@ grpc_security_status grpc_security_context_create_handshaker(
/* Check the peer.
Implementations can choose to check the peer either synchronously or
asynchronously. In the first case, a successful will return
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.
Note:
Asynchronous implementations of this interface should make a copy of the
fields of the peer they want to check as there is no guarantee on the
lifetime of the peer object beyond this call.
Ownership of the peer is transfered.
*/
grpc_security_status grpc_security_context_check_peer(
grpc_security_context *ctx, const tsi_peer *peer,
grpc_security_check_peer_cb cb, void *user_data);
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);
@ -120,14 +115,26 @@ 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);
grpc_credentials *request_metadata_creds, int call_host_check_is_async);
/* For TESTING ONLY!
Creates a fake context that emulates real server security. */
@ -148,7 +155,8 @@ grpc_security_context *grpc_fake_server_security_context_create(void);
*/
grpc_security_status grpc_ssl_channel_security_context_create(
grpc_credentials *request_metadata_creds, const grpc_ssl_config *config,
const char *secure_peer_name, grpc_channel_security_context **ctx);
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.

@ -92,7 +92,7 @@ int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr) {
grpc_resolved_addresses *resolved = NULL;
grpc_tcp_server *tcp = NULL;
size_t i;
int count = 0;
unsigned count = 0;
int port_num = -1;
int port_temp;
@ -127,6 +127,7 @@ int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr) {
if (count != resolved->naddrs) {
gpr_log(GPR_ERROR, "Only %d addresses added out of total %d resolved",
count, resolved->naddrs);
/* if it's an error, don't we want to goto error; here ? */
}
grpc_resolved_addresses_destroy(resolved);

@ -173,7 +173,7 @@ typedef struct census_log_core_local_block {
struct census_log {
int discard_old_records;
/* Number of cores (aka hardware-contexts) */
int num_cores;
unsigned num_cores;
/* number of CENSUS_LOG_2_MAX_RECORD_SIZE blocks in log */
gpr_int32 num_blocks;
cl_block* blocks; /* Block metadata. */
@ -183,7 +183,7 @@ struct census_log {
/* Keeps the state of the reader iterator. A value of 0 indicates that
iterator has reached the end. census_log_init_reader() resets the
value to num_core to restart iteration. */
gpr_int32 read_iterator_state;
gpr_uint32 read_iterator_state;
/* Points to the block being read. If non-NULL, the block is locked for
reading (block_being_read_->reader_lock is held). */
cl_block* block_being_read;

@ -141,7 +141,7 @@ static void record_stats(census_ht* store, census_op_id op_id,
const census_rpc_stats* stats) {
gpr_mu_lock(&g_mu);
if (store != NULL) {
trace_obj* trace = NULL;
census_trace_obj* trace = NULL;
census_internal_lock_trace_store();
trace = census_get_trace_obj_locked(op_id);
if (trace != NULL) {
@ -184,7 +184,7 @@ static void get_stats(census_ht* store, census_aggregated_rpc_stats* data) {
gpr_mu_lock(&g_mu);
if (store != NULL) {
size_t n;
int i, j;
unsigned i, j;
gpr_timespec now = gpr_now();
census_ht_kv* kv = census_ht_get_all_elements(store, &n);
if (kv != NULL) {

@ -32,38 +32,22 @@
*/
#include "src/core/statistics/census_interface.h"
#include "src/core/statistics/census_tracing.h"
#include <stdio.h>
#include <string.h>
#include "src/core/statistics/census_rpc_stats.h"
#include "src/core/statistics/hash_table.h"
#include "src/core/support/string.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/port_platform.h>
#include <grpc/support/sync.h>
#include <grpc/support/time.h>
/* Struct for a trace annotation. */
typedef struct annotation {
gpr_timespec ts; /* timestamp of the annotation */
char txt[CENSUS_MAX_ANNOTATION_LENGTH + 1]; /* actual txt annotation */
struct annotation* next;
} annotation;
typedef struct trace_obj {
census_op_id id;
gpr_timespec ts;
census_rpc_stats rpc_stats;
char* method;
annotation* annotations;
} trace_obj;
static void trace_obj_destroy(trace_obj* obj) {
annotation* p = obj->annotations;
void census_trace_obj_destroy(census_trace_obj* obj) {
census_trace_annotation* p = obj->annotations;
while (p != NULL) {
annotation* next = p->next;
census_trace_annotation* next = p->next;
gpr_free(p);
p = next;
}
@ -71,7 +55,9 @@ static void trace_obj_destroy(trace_obj* obj) {
gpr_free(obj);
}
static void delete_trace_obj(void* obj) { trace_obj_destroy((trace_obj*)obj); }
static void delete_trace_obj(void* obj) {
census_trace_obj_destroy((census_trace_obj*)obj);
}
static const census_ht_option ht_opt = {
CENSUS_HT_UINT64 /* key type*/, 571 /* n_of_buckets */, NULL /* hash */,
@ -103,8 +89,8 @@ static void init_mutex_once(void) {
census_op_id census_tracing_start_op(void) {
gpr_mu_lock(&g_mu);
{
trace_obj* ret = (trace_obj*)gpr_malloc(sizeof(trace_obj));
memset(ret, 0, sizeof(trace_obj));
census_trace_obj* ret = gpr_malloc(sizeof(census_trace_obj));
memset(ret, 0, sizeof(census_trace_obj));
g_id++;
memcpy(&ret->id, &g_id, sizeof(census_op_id));
ret->rpc_stats.cnt = 1;
@ -118,7 +104,7 @@ census_op_id census_tracing_start_op(void) {
int census_add_method_tag(census_op_id op_id, const char* method) {
int ret = 0;
trace_obj* trace = NULL;
census_trace_obj* trace = NULL;
gpr_mu_lock(&g_mu);
trace = census_ht_find(g_trace_store, op_id_as_key(&op_id));
if (trace == NULL) {
@ -131,11 +117,11 @@ int census_add_method_tag(census_op_id op_id, const char* method) {
}
void census_tracing_print(census_op_id op_id, const char* anno_txt) {
trace_obj* trace = NULL;
census_trace_obj* trace = NULL;
gpr_mu_lock(&g_mu);
trace = census_ht_find(g_trace_store, op_id_as_key(&op_id));
if (trace != NULL) {
annotation* anno = gpr_malloc(sizeof(annotation));
census_trace_annotation* anno = gpr_malloc(sizeof(census_trace_annotation));
anno->ts = gpr_now();
{
char* d = anno->txt;
@ -153,7 +139,7 @@ void census_tracing_print(census_op_id op_id, const char* anno_txt) {
}
void census_tracing_end_op(census_op_id op_id) {
trace_obj* trace = NULL;
census_trace_obj* trace = NULL;
gpr_mu_lock(&g_mu);
trace = census_ht_find(g_trace_store, op_id_as_key(&op_id));
if (trace != NULL) {
@ -196,14 +182,58 @@ void census_internal_lock_trace_store(void) { gpr_mu_lock(&g_mu); }
void census_internal_unlock_trace_store(void) { gpr_mu_unlock(&g_mu); }
trace_obj* census_get_trace_obj_locked(census_op_id op_id) {
census_trace_obj* census_get_trace_obj_locked(census_op_id op_id) {
if (g_trace_store == NULL) {
gpr_log(GPR_ERROR, "Census trace store is not initialized.");
return NULL;
}
return (trace_obj*)census_ht_find(g_trace_store, op_id_as_key(&op_id));
return (census_trace_obj*)census_ht_find(g_trace_store, op_id_as_key(&op_id));
}
const char* census_get_trace_method_name(const trace_obj* trace) {
return (const char*)trace->method;
const char* census_get_trace_method_name(const census_trace_obj* trace) {
return trace->method;
}
static census_trace_annotation* dup_annotation_chain(
census_trace_annotation* from) {
census_trace_annotation *ret = NULL;
census_trace_annotation **to = &ret;
for (; from != NULL; from = from->next) {
*to = gpr_malloc(sizeof(census_trace_annotation));
memcpy(*to, from, sizeof(census_trace_annotation));
to = &(*to)->next;
}
return ret;
}
static census_trace_obj* trace_obj_dup(census_trace_obj* from) {
census_trace_obj* to = NULL;
GPR_ASSERT(from != NULL);
to = gpr_malloc(sizeof(census_trace_obj));
to->id = from->id;
to->ts = from->ts;
to->rpc_stats = from->rpc_stats;
to->method = gpr_strdup(from->method);
to->annotations = dup_annotation_chain(from->annotations);
return to;
}
census_trace_obj** census_get_active_ops(int* num_active_ops) {
census_trace_obj** ret = NULL;
gpr_mu_lock(&g_mu);
if (g_trace_store != NULL) {
size_t n = 0;
census_ht_kv* all_kvs = census_ht_get_all_elements(g_trace_store, &n);
*num_active_ops = (int)n;
if (n != 0 ) {
size_t i = 0;
ret = gpr_malloc(sizeof(census_trace_obj *) * n);
for (i = 0; i < n; i++) {
ret[i] = trace_obj_dup((census_trace_obj*)all_kvs[i].v);
}
}
gpr_free(all_kvs);
}
gpr_mu_unlock(&g_mu);
return ret;
}

@ -34,8 +34,35 @@
#ifndef __GRPC_INTERNAL_STATISTICS_CENSUS_TRACING_H_
#define __GRPC_INTERNAL_STATISTICS_CENSUS_TRACING_H_
/* Opaque structure for trace object */
typedef struct trace_obj trace_obj;
#include <grpc/support/time.h>
#include "src/core/statistics/census_rpc_stats.h"
/* WARNING: The data structures and APIs provided by this file are for GRPC
library's internal use ONLY. They might be changed in backward-incompatible
ways and are not subject to any deprecation policy.
They are not recommended for external use.
*/
#ifdef __cplusplus
extern "C" {
#endif
/* Struct for a trace annotation. */
typedef struct census_trace_annotation {
gpr_timespec ts; /* timestamp of the annotation */
char txt[CENSUS_MAX_ANNOTATION_LENGTH + 1]; /* actual txt annotation */
struct census_trace_annotation* next;
} census_trace_annotation;
typedef struct census_trace_obj {
census_op_id id;
gpr_timespec ts;
census_rpc_stats rpc_stats;
char* method;
census_trace_annotation* annotations;
} census_trace_obj;
/* Deletes trace object. */
void census_trace_obj_destroy(census_trace_obj* obj);
/* Initializes trace store. This function is thread safe. */
void census_tracing_init(void);
@ -46,14 +73,24 @@ void census_tracing_shutdown(void);
/* Gets trace obj corresponding to the input op_id. Returns NULL if trace store
is not initialized or trace obj is not found. Requires trace store being
locked before calling this function. */
trace_obj* census_get_trace_obj_locked(census_op_id op_id);
census_trace_obj* census_get_trace_obj_locked(census_op_id op_id);
/* The following two functions acquire and release the trace store global lock.
They are for census internal use only. */
void census_internal_lock_trace_store(void);
void census_internal_unlock_trace_store(void);
/* Gets method tag name associated with the input trace object. */
const char* census_get_trace_method_name(const trace_obj* trace);
/* Gets method name associated with the input trace object. */
const char* census_get_trace_method_name(const census_trace_obj* trace);
/* Returns an array of pointers to trace objects of currently active operations
and fills in number of active operations. Returns NULL if there are no active
operations.
Caller owns the returned objects. */
census_trace_obj** census_get_active_ops(int* num_active_ops);
#ifdef __cplusplus
}
#endif
#endif /* __GRPC_INTERNAL_STATISTICS_CENSUS_TRACING_H_ */

@ -292,7 +292,7 @@ static void ht_delete_entry_chain(const census_ht_option* options,
}
void census_ht_destroy(census_ht* ht) {
int i;
unsigned i;
for (i = 0; i < ht->num_buckets; ++i) {
ht_delete_entry_chain(&ht->options, ht->buckets[i].next);
}

@ -150,7 +150,7 @@ window_stats* census_window_stats_create(int nintervals,
is->width = size_ns / granularity;
/* Check for possible overflow issues, and maximize interval size if the
user requested something large enough. */
if (GPR_INT64_MAX - is->width > size_ns) {
if ((GPR_INT64_MAX - is->width) > size_ns) {
is->top = size_ns + is->width;
} else {
is->top = GPR_INT64_MAX;

@ -38,12 +38,12 @@
/* Return the number of CPU cores on the current system. Will return 0 if
if information is not available. */
int gpr_cpu_num_cores(void);
unsigned gpr_cpu_num_cores(void);
/* Return the CPU on which the current thread is executing; N.B. This should
be considered advisory only - it is possible that the thread is switched
to a different CPU at any time. Returns a value in range
[0, gpr_cpu_num_cores() - 1] */
int gpr_cpu_current_cpu(void);
unsigned gpr_cpu_current_cpu(void);
#endif /* __GRPC_INTERNAL_SUPPORT_CPU_H__ */

@ -31,52 +31,26 @@
*
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif /* _GNU_SOURCE */
#include <grpc/support/port_platform.h>
#ifdef GPR_CPU_LINUX
#include "src/core/support/cpu.h"
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#define GRPC_GNU_SOURCE
#endif
#ifndef __USE_GNU
#define __USE_GNU
#define GRPC_USE_GNU
#endif
#ifndef __USE_MISC
#define __USE_MISC
#define GRPC_USE_MISC
#endif
#include <sched.h>
#ifdef GRPC_GNU_SOURCE
#undef _GNU_SOURCE
#undef GRPC_GNU_SOURCE
#endif
#ifdef GRPC_USE_GNU
#undef __USE_GNU
#undef GRPC_USE_GNU
#endif
#ifdef GRPC_USE_MISC
#undef __USE_MISC
#undef GRPC_USE_MISC
#endif
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <grpc/support/log.h>
int gpr_cpu_num_cores(void) {
unsigned gpr_cpu_num_cores(void) {
static int ncpus = 0;
/* FIXME: !threadsafe */
if (ncpus == 0) {
ncpus = sysconf(_SC_NPROCESSORS_ONLN);
if (ncpus < 1) {
@ -87,7 +61,7 @@ int gpr_cpu_num_cores(void) {
return ncpus;
}
int gpr_cpu_current_cpu(void) {
unsigned gpr_cpu_current_cpu(void) {
int cpu = sched_getcpu();
if (cpu < 0) {
gpr_log(GPR_ERROR, "Error determining current CPU: %s\n", strerror(errno));

@ -45,7 +45,7 @@
static __thread char magic_thread_local;
int gpr_cpu_num_cores(void) {
unsigned gpr_cpu_num_cores(void) {
static int ncpus = 0;
if (ncpus == 0) {
ncpus = sysconf(_SC_NPROCESSORS_ONLN);
@ -63,7 +63,7 @@ static size_t shard_ptr(const void *info) {
return ((x >> 4) ^ (x >> 9) ^ (x >> 14)) % gpr_cpu_num_cores();
}
int gpr_cpu_current_cpu(void) {
unsigned gpr_cpu_current_cpu(void) {
/* NOTE: there's no way I know to return the actual cpu index portably...
most code that's using this is using it to shard across work queues though,
so here we use thread identity instead to achieve a similar though not

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

Loading…
Cancel
Save