Merge github.com:grpc/grpc into scalable-poll

pull/1608/head
Craig Tiller 10 years ago
commit fe46136721
  1. 21
      .travis.yml
  2. 2
      BUILD
  3. 1368
      Makefile
  4. 3
      README.md
  5. 77
      build.json
  6. 2
      examples/pubsub/main.cc
  7. 14
      include/grpc++/client_context.h
  8. 3
      include/grpc++/config.h
  9. 2
      include/grpc++/create_channel.h
  10. 35
      include/grpc++/credentials.h
  11. 6
      include/grpc++/stream.h
  12. 5
      include/grpc/grpc.h
  13. 12
      include/grpc/support/subprocess.h
  14. 77
      src/compiler/csharp_generator.cc
  15. 19
      src/compiler/generator_helpers.h
  16. 307
      src/compiler/objective_c_generator.cc
  17. 9
      src/compiler/objective_c_generator.h
  18. 12
      src/compiler/objective_c_generator_helpers.h
  19. 79
      src/compiler/objective_c_plugin.cc
  20. 10
      src/core/httpcli/httpcli.c
  21. 7
      src/core/iomgr/pollset_posix.c
  22. 6
      src/core/iomgr/sockaddr_utils.c
  23. 4
      src/core/iomgr/tcp_posix.c
  24. 32
      src/core/profiling/basic_timers.c
  25. 13
      src/core/profiling/stap_timers.c
  26. 41
      src/core/profiling/timers.h
  27. 2
      src/core/security/security_connector.c
  28. 4
      src/core/security/security_connector.h
  29. 8
      src/core/support/subprocess_posix.c
  30. 61
      src/core/surface/call.c
  31. 4
      src/core/surface/completion_queue.c
  32. 4
      src/core/surface/server.c
  33. 2
      src/core/transport/chttp2/frame.h
  34. 40
      src/core/transport/chttp2/frame_rst_stream.c
  35. 11
      src/core/transport/chttp2/frame_rst_stream.h
  36. 4
      src/core/transport/chttp2/hpack_parser.c
  37. 2
      src/core/transport/chttp2/hpack_parser.h
  38. 117
      src/core/transport/chttp2_transport.c
  39. 12
      src/cpp/client/client_context.cc
  40. 2
      src/cpp/client/create_channel.cc
  41. 7
      src/cpp/client/insecure_credentials.cc
  42. 32
      src/cpp/client/secure_credentials.cc
  43. 1
      src/cpp/client/secure_credentials.h
  44. 2
      src/cpp/server/server.cc
  45. 1
      src/csharp/Grpc.Auth/Grpc.Auth.csproj
  46. 3
      src/csharp/Grpc.Auth/Grpc.Auth.nuspec
  47. 7
      src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj
  48. 1
      src/csharp/Grpc.Core.Tests/packages.config
  49. 35
      src/csharp/Grpc.Core/AsyncClientStreamingCall.cs
  50. 44
      src/csharp/Grpc.Core/AsyncDuplexStreamingCall.cs
  51. 27
      src/csharp/Grpc.Core/AsyncServerStreamingCall.cs
  52. 2
      src/csharp/Grpc.Core/Call.cs
  53. 6
      src/csharp/Grpc.Core/Calls.cs
  54. 4
      src/csharp/Grpc.Core/Grpc.Core.csproj
  55. 6
      src/csharp/Grpc.Core/Grpc.Core.nuspec
  56. 9
      src/csharp/Grpc.Core/IAsyncStreamReader.cs
  57. 5
      src/csharp/Grpc.Core/IAsyncStreamWriter.cs
  58. 5
      src/csharp/Grpc.Core/IClientStreamWriter.cs
  59. 2
      src/csharp/Grpc.Core/IServerStreamWriter.cs
  60. 6
      src/csharp/Grpc.Core/Internal/ClientRequestStream.cs
  61. 29
      src/csharp/Grpc.Core/Internal/ClientResponseStream.cs
  62. 25
      src/csharp/Grpc.Core/Internal/ServerCallHandler.cs
  63. 29
      src/csharp/Grpc.Core/Internal/ServerRequestStream.cs
  64. 4
      src/csharp/Grpc.Core/Internal/ServerResponseStream.cs
  65. 3
      src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs
  66. 1
      src/csharp/Grpc.Core/ServerCallContext.cs
  67. 30
      src/csharp/Grpc.Core/Utils/AsyncStreamExtensions.cs
  68. 1
      src/csharp/Grpc.Core/packages.config
  69. 4
      src/csharp/Grpc.Examples.Tests/Grpc.Examples.Tests.csproj
  70. 51
      src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs
  71. 1
      src/csharp/Grpc.Examples.Tests/packages.config
  72. 3
      src/csharp/Grpc.Examples/Grpc.Examples.csproj
  73. 40
      src/csharp/Grpc.Examples/MathExamples.cs
  74. 44
      src/csharp/Grpc.Examples/MathGrpc.cs
  75. 4
      src/csharp/Grpc.Examples/MathServiceImpl.cs
  76. 1
      src/csharp/Grpc.Examples/packages.config
  77. 3
      src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj
  78. 168
      src/csharp/Grpc.IntegrationTesting/InteropClient.cs
  79. 70
      src/csharp/Grpc.IntegrationTesting/TestGrpc.cs
  80. 4
      src/csharp/Grpc.IntegrationTesting/TestServiceImpl.cs
  81. 1
      src/csharp/Grpc.IntegrationTesting/packages.config
  82. 4
      src/csharp/build_packages.bat
  83. 16
      src/node/bin/README.md
  84. 2
      src/node/bin/service_packager
  85. 142
      src/node/cli/service_packager.js
  86. 36
      src/node/cli/service_packager/index.js
  87. 17
      src/node/cli/service_packager/package.json.template
  88. 6
      src/node/interop/interop_client.js
  89. 3
      src/node/package.json
  90. 58
      src/node/src/client.js
  91. 29
      src/node/src/server.js
  92. 28
      src/node/test/end_to_end_test.js
  93. 224
      src/node/test/surface_test.js
  94. 6
      src/objective-c/GRPCClient/GRPCCall.m
  95. 17
      src/objective-c/GRPCClient/private/GRPCDelegateWrapper.h
  96. 8
      src/objective-c/GRPCClient/private/GRPCDelegateWrapper.m
  97. 4
      src/objective-c/ProtoRPC/ProtoRPC.m
  98. 48
      src/objective-c/README.md
  99. 59
      src/objective-c/RxLibrary/GRXBufferedPipe.h
  100. 146
      src/objective-c/RxLibrary/GRXBufferedPipe.m
  101. Some files were not shown because too many files have changed in this diff Show More

@ -17,22 +17,23 @@ env:
- CPPFLAGS=-I/tmp/prebuilt/include - CPPFLAGS=-I/tmp/prebuilt/include
- NUGET="mono nuget.exe" - NUGET="mono nuget.exe"
matrix: matrix:
- CONFIG=opt TEST=sanity - CONFIG=opt TEST=sanity JOBS=1
- CONFIG=gcov TEST="c c++" - CONFIG=gcov TEST=c JOBS=16
- CONFIG=opt TEST="c c++" - CONFIG=gcov TEST=c++ JOBS=16
- CONFIG=opt TEST=node - CONFIG=opt TEST="c c++" JOBS=16
- CONFIG=opt TEST=ruby - CONFIG=opt TEST=node JOBS=16
- CONFIG=opt TEST=python - CONFIG=opt TEST=ruby JOBS=16
- CONFIG=opt TEST=csharp - CONFIG=opt TEST=python JOBS=1
- USE_GCC=4.4 CONFIG=opt TEST=build - CONFIG=opt TEST=csharp JOBS=16
- USE_GCC=4.4 CONFIG=opt TEST=build JOBS=16
script: script:
- rvm use $RUBY_VERSION - rvm use $RUBY_VERSION
- gem install bundler - gem install bundler
- ./tools/run_tests/prepare_travis.sh - ./tools/run_tests/prepare_travis.sh
- if [ ! -z "$USE_GCC" ] ; then export CC=gcc-$USE_GCC ; export CXX=g++-$USE_GCC ; fi - if [ ! -z "$USE_GCC" ] ; then export CC=gcc-$USE_GCC ; export CXX=g++-$USE_GCC ; fi
- ./tools/run_tests/run_tests.py -l $TEST -t -j 16 -c $CONFIG -s 4.0 - ./tools/run_tests/run_tests.py -l $TEST -t -j $JOBS -c $CONFIG -s 4.0
after_success: after_success:
- if [ "$CONFIG" = "gcov" ] ; then coveralls --exclude third_party --exclude gens -b. --gcov-options '\-p' ; fi - if [ "$CONFIG" = "gcov" ] ; then coveralls --exclude third_party --exclude gens --exclude test --exclude src/compiler -b. --gcov-options '\-p' ; fi
notifications: notifications:
email: false email: false
webhooks: webhooks:

@ -354,7 +354,6 @@ cc_library(
"include/grpc/byte_buffer.h", "include/grpc/byte_buffer.h",
"include/grpc/byte_buffer_reader.h", "include/grpc/byte_buffer_reader.h",
"include/grpc/grpc.h", "include/grpc/grpc.h",
"include/grpc/grpc_http.h",
"include/grpc/status.h", "include/grpc/status.h",
], ],
includes = [ includes = [
@ -561,7 +560,6 @@ cc_library(
"include/grpc/byte_buffer.h", "include/grpc/byte_buffer.h",
"include/grpc/byte_buffer_reader.h", "include/grpc/byte_buffer_reader.h",
"include/grpc/grpc.h", "include/grpc/grpc.h",
"include/grpc/grpc_http.h",
"include/grpc/status.h", "include/grpc/status.h",
], ],
includes = [ includes = [

1368
Makefile

File diff suppressed because one or more lines are too long

@ -1,4 +1,5 @@
[![Build Status](https://travis-ci.org/grpc/grpc.svg?branch=master)](https://travis-ci.org/grpc/grpc) [![Build Status](https://travis-ci.org/grpc/grpc.svg?branch=master)](https://travis-ci.org/grpc/grpc)
[![Coverage Status](https://img.shields.io/coveralls/grpc/grpc.svg)](https://coveralls.io/r/grpc/grpc?branch=master)
[gRPC - An RPC library and framework](http://github.com/grpc/grpc) [gRPC - An RPC library and framework](http://github.com/grpc/grpc)
=================================== ===================================
@ -37,7 +38,7 @@ Libraries in different languages are in different state of development. We are s
* C++ Library: [src/cpp] (src/cpp) : Early adopter ready - Alpha. * C++ Library: [src/cpp] (src/cpp) : Early adopter ready - Alpha.
* Ruby Library: [src/ruby] (src/ruby) : Early adopter ready - Alpha. * Ruby Library: [src/ruby] (src/ruby) : Early adopter ready - Alpha.
* NodeJS Library: [src/node] (src/node) : Early adopter ready - Alpha. * NodeJS Library: [src/node] (src/node) : Early adopter ready - Alpha.
* Python Library: [src/python] (src/python) : Usable with limitations - Alpha. * Python Library: [src/python] (src/python) : Early adopter ready - Alpha.
* PHP Library: [src/php] (src/php) : Pre-Alpha. * PHP Library: [src/php] (src/php) : Pre-Alpha.
* C# Library: [src/csharp] (src/csharp) : Pre-Alpha. * C# Library: [src/csharp] (src/csharp) : Pre-Alpha.
* Objective-C Library: [src/objective-c] (src/objective-c): Pre-Alpha. * Objective-C Library: [src/objective-c] (src/objective-c): Pre-Alpha.

@ -6,7 +6,7 @@
"#": "The public version number of the library.", "#": "The public version number of the library.",
"version": { "version": {
"major": 0, "major": 0,
"minor": 8, "minor": 9,
"micro": 0, "micro": 0,
"build": 0 "build": 0
} }
@ -88,7 +88,6 @@
"include/grpc/byte_buffer.h", "include/grpc/byte_buffer.h",
"include/grpc/byte_buffer_reader.h", "include/grpc/byte_buffer_reader.h",
"include/grpc/grpc.h", "include/grpc/grpc.h",
"include/grpc/grpc_http.h",
"include/grpc/status.h" "include/grpc/status.h"
], ],
"headers": [ "headers": [
@ -543,7 +542,9 @@
"test/cpp/util/echo.proto", "test/cpp/util/echo.proto",
"test/cpp/util/echo_duplicate.proto", "test/cpp/util/echo_duplicate.proto",
"test/cpp/util/cli_call.cc", "test/cpp/util/cli_call.cc",
"test/cpp/util/create_test_channel.cc" "test/cpp/util/create_test_channel.cc",
"test/cpp/util/fake_credentials.cc",
"test/cpp/util/subprocess.cc"
] ]
}, },
{ {
@ -1813,7 +1814,6 @@
{ {
"name": "async_streaming_ping_pong_test", "name": "async_streaming_ping_pong_test",
"build": "test", "build": "test",
"run": false,
"language": "c++", "language": "c++",
"src": [ "src": [
"test/cpp/qps/async_streaming_ping_pong_test.cc" "test/cpp/qps/async_streaming_ping_pong_test.cc"
@ -1831,7 +1831,6 @@
{ {
"name": "async_unary_ping_pong_test", "name": "async_unary_ping_pong_test",
"build": "test", "build": "test",
"run": false,
"language": "c++", "language": "c++",
"src": [ "src": [
"test/cpp/qps/async_unary_ping_pong_test.cc" "test/cpp/qps/async_unary_ping_pong_test.cc"
@ -1875,6 +1874,39 @@
"gpr" "gpr"
] ]
}, },
{
"name": "client_crash_test",
"build": "test",
"language": "c++",
"src": [
"test/cpp/end2end/client_crash_test.cc"
],
"deps": [
"grpc++_test_util",
"grpc_test_util",
"grpc++",
"grpc",
"gpr_test_util",
"gpr"
]
},
{
"name": "client_crash_test_server",
"build": "test",
"run": false,
"language": "c++",
"src": [
"test/cpp/end2end/client_crash_test_server.cc"
],
"deps": [
"grpc++_test_util",
"grpc_test_util",
"grpc++",
"grpc",
"gpr_test_util",
"gpr"
]
},
{ {
"name": "credentials_test", "name": "credentials_test",
"build": "test", "build": "test",
@ -2189,6 +2221,39 @@
"grpc++_test_config" "grpc++_test_config"
] ]
}, },
{
"name": "server_crash_test",
"build": "test",
"language": "c++",
"src": [
"test/cpp/end2end/server_crash_test.cc"
],
"deps": [
"grpc++_test_util",
"grpc_test_util",
"grpc++",
"grpc",
"gpr_test_util",
"gpr"
]
},
{
"name": "server_crash_test_client",
"build": "test",
"run": false,
"language": "c++",
"src": [
"test/cpp/end2end/server_crash_test_client.cc"
],
"deps": [
"grpc++_test_util",
"grpc_test_util",
"grpc++",
"grpc",
"gpr_test_util",
"gpr"
]
},
{ {
"name": "status_test", "name": "status_test",
"build": "test", "build": "test",
@ -2207,7 +2272,6 @@
{ {
"name": "sync_streaming_ping_pong_test", "name": "sync_streaming_ping_pong_test",
"build": "test", "build": "test",
"run": false,
"language": "c++", "language": "c++",
"src": [ "src": [
"test/cpp/qps/sync_streaming_ping_pong_test.cc" "test/cpp/qps/sync_streaming_ping_pong_test.cc"
@ -2225,7 +2289,6 @@
{ {
"name": "sync_unary_ping_pong_test", "name": "sync_unary_ping_pong_test",
"build": "test", "build": "test",
"run": false,
"language": "c++", "language": "c++",
"src": [ "src": [
"test/cpp/qps/sync_unary_ping_pong_test.cc" "test/cpp/qps/sync_unary_ping_pong_test.cc"

@ -71,7 +71,7 @@ int main(int argc, char** argv) {
ss << FLAGS_server_host << ":" << FLAGS_server_port; ss << FLAGS_server_host << ":" << FLAGS_server_port;
std::unique_ptr<grpc::Credentials> creds = grpc::GoogleDefaultCredentials(); std::shared_ptr<grpc::Credentials> creds = grpc::GoogleDefaultCredentials();
std::shared_ptr<grpc::ChannelInterface> channel = std::shared_ptr<grpc::ChannelInterface> channel =
grpc::CreateChannel(ss.str(), creds, grpc::ChannelArguments()); grpc::CreateChannel(ss.str(), creds, grpc::ChannelArguments());

@ -51,6 +51,7 @@ namespace grpc {
class CallOpBuffer; class CallOpBuffer;
class ChannelInterface; class ChannelInterface;
class CompletionQueue; class CompletionQueue;
class Credentials;
class RpcMethod; class RpcMethod;
class Status; class Status;
template <class R> template <class R>
@ -102,6 +103,11 @@ class ClientContext {
void set_authority(const grpc::string& authority) { authority_ = authority; } void set_authority(const grpc::string& authority) { authority_ = authority; }
// Set credentials for the rpc.
void set_credentials(const std::shared_ptr<Credentials>& creds) {
creds_ = creds;
}
void TryCancel(); void TryCancel();
private: private:
@ -127,11 +133,8 @@ class ClientContext {
friend class ::grpc::ClientAsyncResponseReader; friend class ::grpc::ClientAsyncResponseReader;
grpc_call* call() { return call_; } grpc_call* call() { return call_; }
void set_call(grpc_call* call, const std::shared_ptr<ChannelInterface>& channel) { void set_call(grpc_call* call,
GPR_ASSERT(call_ == nullptr); const std::shared_ptr<ChannelInterface>& channel);
call_ = call;
channel_ = channel;
}
grpc_completion_queue* cq() { return cq_; } grpc_completion_queue* cq() { return cq_; }
void set_cq(grpc_completion_queue* cq) { cq_ = cq; } void set_cq(grpc_completion_queue* cq) { cq_ = cq; }
@ -144,6 +147,7 @@ class ClientContext {
grpc_completion_queue* cq_; grpc_completion_queue* cq_;
gpr_timespec deadline_; gpr_timespec deadline_;
grpc::string authority_; grpc::string authority_;
std::shared_ptr<Credentials> creds_;
std::multimap<grpc::string, grpc::string> send_initial_metadata_; std::multimap<grpc::string, grpc::string> send_initial_metadata_;
std::multimap<grpc::string, grpc::string> recv_initial_metadata_; std::multimap<grpc::string, grpc::string> recv_initial_metadata_;
std::multimap<grpc::string, grpc::string> trailing_metadata_; std::multimap<grpc::string, grpc::string> trailing_metadata_;

@ -112,6 +112,9 @@ public:
template <class T> operator std::unique_ptr<T>() const { template <class T> operator std::unique_ptr<T>() const {
return std::unique_ptr<T>(static_cast<T *>(0)); return std::unique_ptr<T>(static_cast<T *>(0));
} }
template <class T> operator std::shared_ptr<T>() const {
return std::shared_ptr<T>(static_cast<T *>(0));
}
operator bool() const {return false;} operator bool() const {return false;}
private: private:
void operator&() const = delete; void operator&() const = delete;

@ -45,7 +45,7 @@ class ChannelInterface;
// If creds does not hold an object or is invalid, a lame channel is returned. // If creds does not hold an object or is invalid, a lame channel is returned.
std::shared_ptr<ChannelInterface> CreateChannel( std::shared_ptr<ChannelInterface> CreateChannel(
const grpc::string& target, const std::unique_ptr<Credentials>& creds, const grpc::string& target, const std::shared_ptr<Credentials>& creds,
const ChannelArguments& args); const ChannelArguments& args);
} // namespace grpc } // namespace grpc

@ -47,17 +47,18 @@ class SecureCredentials;
class Credentials : public GrpcLibrary { class Credentials : public GrpcLibrary {
public: public:
~Credentials() GRPC_OVERRIDE; ~Credentials() GRPC_OVERRIDE;
virtual bool ApplyToCall(grpc_call* call) = 0;
protected: protected:
friend std::unique_ptr<Credentials> CompositeCredentials( friend std::shared_ptr<Credentials> CompositeCredentials(
const std::unique_ptr<Credentials>& creds1, const std::shared_ptr<Credentials>& creds1,
const std::unique_ptr<Credentials>& creds2); const std::shared_ptr<Credentials>& creds2);
virtual SecureCredentials* AsSecureCredentials() = 0; virtual SecureCredentials* AsSecureCredentials() = 0;
private: private:
friend std::shared_ptr<ChannelInterface> CreateChannel( friend std::shared_ptr<ChannelInterface> CreateChannel(
const grpc::string& target, const std::unique_ptr<Credentials>& creds, const grpc::string& target, const std::shared_ptr<Credentials>& creds,
const ChannelArguments& args); const ChannelArguments& args);
virtual std::shared_ptr<ChannelInterface> CreateChannel( virtual std::shared_ptr<ChannelInterface> CreateChannel(
@ -80,20 +81,20 @@ struct SslCredentialsOptions {
}; };
// Factories for building different types of Credentials // Factories for building different types of Credentials
// The functions may return empty unique_ptr when credentials cannot be created. // The functions may return empty shared_ptr when credentials cannot be created.
// If a Credentials pointer is returned, it can still be invalid when used to // If a Credentials pointer is returned, it can still be invalid when used to
// create a channel. A lame channel will be created then and all rpcs will // create a channel. A lame channel will be created then and all rpcs will
// fail on it. // fail on it.
// Builds credentials with reasonable defaults. // Builds credentials with reasonable defaults.
std::unique_ptr<Credentials> GoogleDefaultCredentials(); std::shared_ptr<Credentials> GoogleDefaultCredentials();
// Builds SSL Credentials given SSL specific options // Builds SSL Credentials given SSL specific options
std::unique_ptr<Credentials> SslCredentials( std::shared_ptr<Credentials> SslCredentials(
const SslCredentialsOptions& options); const SslCredentialsOptions& options);
// Builds credentials for use when running in GCE // Builds credentials for use when running in GCE
std::unique_ptr<Credentials> ComputeEngineCredentials(); std::shared_ptr<Credentials> ComputeEngineCredentials();
// Builds service account credentials. // Builds service account credentials.
// json_key is the JSON key string containing the client's private key. // json_key is the JSON key string containing the client's private key.
@ -101,7 +102,7 @@ std::unique_ptr<Credentials> ComputeEngineCredentials();
// token_lifetime_seconds is the lifetime in seconds of each token acquired // token_lifetime_seconds is the lifetime in seconds of each token acquired
// through this service account credentials. It should be positive and should // through this service account credentials. It should be positive and should
// not exceed grpc_max_auth_token_lifetime or will be cropped to this value. // not exceed grpc_max_auth_token_lifetime or will be cropped to this value.
std::unique_ptr<Credentials> ServiceAccountCredentials( std::shared_ptr<Credentials> ServiceAccountCredentials(
const grpc::string& json_key, const grpc::string& scope, const grpc::string& json_key, const grpc::string& scope,
long token_lifetime_seconds); long token_lifetime_seconds);
@ -110,27 +111,27 @@ std::unique_ptr<Credentials> ServiceAccountCredentials(
// token_lifetime_seconds is the lifetime in seconds of each Json Web Token // token_lifetime_seconds is the lifetime in seconds of each Json Web Token
// (JWT) created with this credentials. It should not exceed // (JWT) created with this credentials. It should not exceed
// grpc_max_auth_token_lifetime or will be cropped to this value. // grpc_max_auth_token_lifetime or will be cropped to this value.
std::unique_ptr<Credentials> JWTCredentials( std::shared_ptr<Credentials> JWTCredentials(const grpc::string& json_key,
const grpc::string& json_key, long token_lifetime_seconds); long token_lifetime_seconds);
// Builds refresh token credentials. // Builds refresh token credentials.
// json_refresh_token is the JSON string containing the refresh token along // json_refresh_token is the JSON string containing the refresh token along
// with a client_id and client_secret. // with a client_id and client_secret.
std::unique_ptr<Credentials> RefreshTokenCredentials( std::shared_ptr<Credentials> RefreshTokenCredentials(
const grpc::string& json_refresh_token); const grpc::string& json_refresh_token);
// Builds IAM credentials. // Builds IAM credentials.
std::unique_ptr<Credentials> IAMCredentials( std::shared_ptr<Credentials> IAMCredentials(
const grpc::string& authorization_token, const grpc::string& authorization_token,
const grpc::string& authority_selector); const grpc::string& authority_selector);
// Combines two credentials objects into a composite credentials // Combines two credentials objects into a composite credentials
std::unique_ptr<Credentials> CompositeCredentials( std::shared_ptr<Credentials> CompositeCredentials(
const std::unique_ptr<Credentials>& creds1, const std::shared_ptr<Credentials>& creds1,
const std::unique_ptr<Credentials>& creds2); const std::shared_ptr<Credentials>& creds2);
// Credentials for an unencrypted, unauthenticated channel // Credentials for an unencrypted, unauthenticated channel
std::unique_ptr<Credentials> InsecureCredentials(); std::shared_ptr<Credentials> InsecureCredentials();
} // namespace grpc } // namespace grpc

@ -114,7 +114,7 @@ class ClientReader GRPC_FINAL : public ClientReaderInterface<R> {
CallOpBuffer buf; CallOpBuffer buf;
buf.AddRecvInitialMetadata(context_); buf.AddRecvInitialMetadata(context_);
call_.PerformOps(&buf); call_.PerformOps(&buf);
GPR_ASSERT(cq_.Pluck(&buf)); cq_.Pluck(&buf); // status ignored
} }
bool Read(R* msg) GRPC_OVERRIDE { bool Read(R* msg) GRPC_OVERRIDE {
@ -216,7 +216,7 @@ class ClientReaderWriter GRPC_FINAL : public ClientReaderWriterInterface<W, R> {
CallOpBuffer buf; CallOpBuffer buf;
buf.AddSendInitialMetadata(&context->send_initial_metadata_); buf.AddSendInitialMetadata(&context->send_initial_metadata_);
call_.PerformOps(&buf); call_.PerformOps(&buf);
GPR_ASSERT(cq_.Pluck(&buf)); cq_.Pluck(&buf);
} }
// Blocking wait for initial metadata from server. The received metadata // Blocking wait for initial metadata from server. The received metadata
@ -229,7 +229,7 @@ class ClientReaderWriter GRPC_FINAL : public ClientReaderWriterInterface<W, R> {
CallOpBuffer buf; CallOpBuffer buf;
buf.AddRecvInitialMetadata(context_); buf.AddRecvInitialMetadata(context_);
call_.PerformOps(&buf); call_.PerformOps(&buf);
GPR_ASSERT(cq_.Pluck(&buf)); cq_.Pluck(&buf); // status ignored
} }
bool Read(R* msg) GRPC_OVERRIDE { bool Read(R* msg) GRPC_OVERRIDE {

@ -244,7 +244,10 @@ typedef enum {
GRPC_OP_RECV_INITIAL_METADATA, GRPC_OP_RECV_INITIAL_METADATA,
/* Receive a message: 0 or more of these operations can occur for each call */ /* Receive a message: 0 or more of these operations can occur for each call */
GRPC_OP_RECV_MESSAGE, GRPC_OP_RECV_MESSAGE,
/* Receive status on the client: one and only one must be made on the client /* Receive status on the client: one and only one must be made on the client.
This operation always succeeds, meaning ops paired with this operation
will also appear to succeed, even though they may not have. In that case
the status will indicate some failure.
*/ */
GRPC_OP_RECV_STATUS_ON_CLIENT, GRPC_OP_RECV_STATUS_ON_CLIENT,
/* Receive status on the server: one and only one must be made on the server /* Receive status on the server: one and only one must be made on the server

@ -34,16 +34,24 @@
#ifndef GRPC_SUPPORT_SUBPROCESS_H #ifndef GRPC_SUPPORT_SUBPROCESS_H
#define GRPC_SUPPORT_SUBPROCESS_H #define GRPC_SUPPORT_SUBPROCESS_H
#ifdef __cplusplus
extern "C" {
#endif
typedef struct gpr_subprocess gpr_subprocess; typedef struct gpr_subprocess gpr_subprocess;
/* .exe on windows, empty on unices */ /* .exe on windows, empty on unices */
char *gpr_subprocess_binary_extension(); const char *gpr_subprocess_binary_extension();
gpr_subprocess *gpr_subprocess_create(int argc, char **argv); gpr_subprocess *gpr_subprocess_create(int argc, const char **argv);
/* if subprocess has not been joined, kill it */ /* if subprocess has not been joined, kill it */
void gpr_subprocess_destroy(gpr_subprocess *p); void gpr_subprocess_destroy(gpr_subprocess *p);
/* returns exit status; can be called at most once */ /* returns exit status; can be called at most once */
int gpr_subprocess_join(gpr_subprocess *p); int gpr_subprocess_join(gpr_subprocess *p);
void gpr_subprocess_interrupt(gpr_subprocess *p); void gpr_subprocess_interrupt(gpr_subprocess *p);
#ifdef __cplusplus
} // extern "C"
#endif
#endif #endif

@ -51,20 +51,49 @@ using grpc_generator::METHODTYPE_NO_STREAMING;
using grpc_generator::METHODTYPE_CLIENT_STREAMING; using grpc_generator::METHODTYPE_CLIENT_STREAMING;
using grpc_generator::METHODTYPE_SERVER_STREAMING; using grpc_generator::METHODTYPE_SERVER_STREAMING;
using grpc_generator::METHODTYPE_BIDI_STREAMING; using grpc_generator::METHODTYPE_BIDI_STREAMING;
using grpc_generator::StringReplace;
using std::map; using std::map;
using std::vector; using std::vector;
namespace grpc_csharp_generator { namespace grpc_csharp_generator {
namespace { namespace {
std::string GetCSharpNamespace(const FileDescriptor* file) { // TODO(jtattermusch): make GetFileNamespace part of libprotoc public API.
// TODO(jtattermusch): this should be based on csharp_namespace option // NOTE: Implementation needs to match exactly to GetFileNamespace
// defined in csharp_helpers.h in protoc csharp plugin.
// We cannot reference it directly because google3 protobufs
// don't have a csharp protoc plugin.
std::string GetFileNamespace(const FileDescriptor* file) {
if (file->options().has_csharp_namespace()) {
return file->options().csharp_namespace();
}
return file->package(); return file->package();
} }
std::string GetMessageType(const Descriptor* message) { std::string ToCSharpName(const std::string& name, const FileDescriptor* file) {
// TODO(jtattermusch): this has to match with C# protobuf generator std::string result = GetFileNamespace(file);
return message->name(); if (result != "") {
result += '.';
}
std::string classname;
if (file->package().empty()) {
classname = name;
} else {
// Strip the proto package from full_name since we've replaced it with
// the C# namespace.
classname = name.substr(file->package().size() + 1);
}
result += StringReplace(classname, ".", ".Types.", false);
return "global::" + result;
}
// TODO(jtattermusch): make GetClassName part of libprotoc public API.
// NOTE: Implementation needs to match exactly to GetClassName
// defined in csharp_helpers.h in protoc csharp plugin.
// We cannot reference it directly because google3 protobufs
// don't have a csharp protoc plugin.
std::string GetClassName(const Descriptor* message) {
return ToCSharpName(message->full_name(), message->file());
} }
std::string GetServiceClassName(const ServiceDescriptor* service) { std::string GetServiceClassName(const ServiceDescriptor* service) {
@ -114,22 +143,22 @@ std::string GetMethodRequestParamMaybe(const MethodDescriptor *method) {
if (method->client_streaming()) { if (method->client_streaming()) {
return ""; return "";
} }
return GetMessageType(method->input_type()) + " request, "; return GetClassName(method->input_type()) + " request, ";
} }
std::string GetMethodReturnTypeClient(const MethodDescriptor *method) { std::string GetMethodReturnTypeClient(const MethodDescriptor *method) {
switch (GetMethodType(method)) { switch (GetMethodType(method)) {
case METHODTYPE_NO_STREAMING: case METHODTYPE_NO_STREAMING:
return "Task<" + GetMessageType(method->output_type()) + ">"; return "Task<" + GetClassName(method->output_type()) + ">";
case METHODTYPE_CLIENT_STREAMING: case METHODTYPE_CLIENT_STREAMING:
return "AsyncClientStreamingCall<" + GetMessageType(method->input_type()) return "AsyncClientStreamingCall<" + GetClassName(method->input_type())
+ ", " + GetMessageType(method->output_type()) + ">"; + ", " + GetClassName(method->output_type()) + ">";
case METHODTYPE_SERVER_STREAMING: case METHODTYPE_SERVER_STREAMING:
return "AsyncServerStreamingCall<" + GetMessageType(method->output_type()) return "AsyncServerStreamingCall<" + GetClassName(method->output_type())
+ ">"; + ">";
case METHODTYPE_BIDI_STREAMING: case METHODTYPE_BIDI_STREAMING:
return "AsyncDuplexStreamingCall<" + GetMessageType(method->input_type()) return "AsyncDuplexStreamingCall<" + GetClassName(method->input_type())
+ ", " + GetMessageType(method->output_type()) + ">"; + ", " + GetClassName(method->output_type()) + ">";
} }
GOOGLE_LOG(FATAL)<< "Can't get here."; GOOGLE_LOG(FATAL)<< "Can't get here.";
return ""; return "";
@ -139,10 +168,10 @@ std::string GetMethodRequestParamServer(const MethodDescriptor *method) {
switch (GetMethodType(method)) { switch (GetMethodType(method)) {
case METHODTYPE_NO_STREAMING: case METHODTYPE_NO_STREAMING:
case METHODTYPE_SERVER_STREAMING: case METHODTYPE_SERVER_STREAMING:
return GetMessageType(method->input_type()) + " request"; return GetClassName(method->input_type()) + " request";
case METHODTYPE_CLIENT_STREAMING: case METHODTYPE_CLIENT_STREAMING:
case METHODTYPE_BIDI_STREAMING: case METHODTYPE_BIDI_STREAMING:
return "IAsyncStreamReader<" + GetMessageType(method->input_type()) return "IAsyncStreamReader<" + GetClassName(method->input_type())
+ "> requestStream"; + "> requestStream";
} }
GOOGLE_LOG(FATAL)<< "Can't get here."; GOOGLE_LOG(FATAL)<< "Can't get here.";
@ -153,7 +182,7 @@ std::string GetMethodReturnTypeServer(const MethodDescriptor *method) {
switch (GetMethodType(method)) { switch (GetMethodType(method)) {
case METHODTYPE_NO_STREAMING: case METHODTYPE_NO_STREAMING:
case METHODTYPE_CLIENT_STREAMING: case METHODTYPE_CLIENT_STREAMING:
return "Task<" + GetMessageType(method->output_type()) + ">"; return "Task<" + GetClassName(method->output_type()) + ">";
case METHODTYPE_SERVER_STREAMING: case METHODTYPE_SERVER_STREAMING:
case METHODTYPE_BIDI_STREAMING: case METHODTYPE_BIDI_STREAMING:
return "Task"; return "Task";
@ -169,7 +198,7 @@ std::string GetMethodResponseStreamMaybe(const MethodDescriptor *method) {
return ""; return "";
case METHODTYPE_SERVER_STREAMING: case METHODTYPE_SERVER_STREAMING:
case METHODTYPE_BIDI_STREAMING: case METHODTYPE_BIDI_STREAMING:
return ", IServerStreamWriter<" + GetMessageType(method->output_type()) return ", IServerStreamWriter<" + GetClassName(method->output_type())
+ "> responseStream"; + "> responseStream";
} }
GOOGLE_LOG(FATAL)<< "Can't get here."; GOOGLE_LOG(FATAL)<< "Can't get here.";
@ -202,7 +231,7 @@ void GenerateMarshallerFields(Printer* out, const ServiceDescriptor *service) {
out->Print( out->Print(
"static readonly Marshaller<$type$> $fieldname$ = Marshallers.Create((arg) => arg.ToByteArray(), $type$.ParseFrom);\n", "static readonly Marshaller<$type$> $fieldname$ = Marshallers.Create((arg) => arg.ToByteArray(), $type$.ParseFrom);\n",
"fieldname", GetMarshallerFieldName(message), "type", "fieldname", GetMarshallerFieldName(message), "type",
GetMessageType(message)); GetClassName(message));
} }
out->Print("\n"); out->Print("\n");
} }
@ -211,8 +240,8 @@ void GenerateStaticMethodField(Printer* out, const MethodDescriptor *method) {
out->Print( out->Print(
"static readonly Method<$request$, $response$> $fieldname$ = new Method<$request$, $response$>(\n", "static readonly Method<$request$, $response$> $fieldname$ = new Method<$request$, $response$>(\n",
"fieldname", GetMethodFieldName(method), "request", "fieldname", GetMethodFieldName(method), "request",
GetMessageType(method->input_type()), "response", GetClassName(method->input_type()), "response",
GetMessageType(method->output_type())); GetClassName(method->output_type()));
out->Indent(); out->Indent();
out->Indent(); out->Indent();
out->Print("$methodtype$,\n", "methodtype", out->Print("$methodtype$,\n", "methodtype",
@ -242,8 +271,8 @@ void GenerateClientInterface(Printer* out, const ServiceDescriptor *service) {
out->Print( out->Print(
"$response$ $methodname$($request$ request, CancellationToken token = default(CancellationToken));\n", "$response$ $methodname$($request$ request, CancellationToken token = default(CancellationToken));\n",
"methodname", method->name(), "request", "methodname", method->name(), "request",
GetMessageType(method->input_type()), "response", GetClassName(method->input_type()), "response",
GetMessageType(method->output_type())); GetClassName(method->output_type()));
} }
std::string method_name = method->name(); std::string method_name = method->name();
@ -310,8 +339,8 @@ void GenerateClientStub(Printer* out, const ServiceDescriptor *service) {
out->Print( out->Print(
"public $response$ $methodname$($request$ request, CancellationToken token = default(CancellationToken))\n", "public $response$ $methodname$($request$ request, CancellationToken token = default(CancellationToken))\n",
"methodname", method->name(), "request", "methodname", method->name(), "request",
GetMessageType(method->input_type()), "response", GetClassName(method->input_type()), "response",
GetMessageType(method->output_type())); GetClassName(method->output_type()));
out->Print("{\n"); out->Print("{\n");
out->Indent(); out->Indent();
out->Print("var call = CreateCall($servicenamefield$, $methodfield$);\n", out->Print("var call = CreateCall($servicenamefield$, $methodfield$);\n",
@ -466,7 +495,7 @@ grpc::string GetServices(const FileDescriptor *file) {
// TODO(jtattermusch): add using for protobuf message classes // TODO(jtattermusch): add using for protobuf message classes
out.Print("\n"); out.Print("\n");
out.Print("namespace $namespace$ {\n", "namespace", GetCSharpNamespace(file)); out.Print("namespace $namespace$ {\n", "namespace", GetFileNamespace(file));
out.Indent(); out.Indent();
for (int i = 0; i < file->service_count(); i++) { for (int i = 0; i < file->service_count(); i++) {
GenerateService(&out, file->service(i)); GenerateService(&out, file->service(i));

@ -60,21 +60,26 @@ inline grpc::string StripProto(grpc::string filename) {
} }
inline grpc::string StringReplace(grpc::string str, const grpc::string &from, inline grpc::string StringReplace(grpc::string str, const grpc::string &from,
const grpc::string &to) { const grpc::string &to, bool replace_all) {
size_t pos = 0; size_t pos = 0;
for (;;) { do {
pos = str.find(from, pos); pos = str.find(from, pos);
if (pos == grpc::string::npos) { if (pos == grpc::string::npos) {
break; break;
} }
str.replace(pos, from.length(), to); str.replace(pos, from.length(), to);
pos += to.length(); pos += to.length();
} } while(replace_all);
return str; return str;
} }
inline grpc::string StringReplace(grpc::string str, const grpc::string &from,
const grpc::string &to) {
return StringReplace(str, from, to, true);
}
inline std::vector<grpc::string> tokenize(const grpc::string &input, inline std::vector<grpc::string> tokenize(const grpc::string &input,
const grpc::string &delimiters) { const grpc::string &delimiters) {
std::vector<grpc::string> tokens; std::vector<grpc::string> tokens;
@ -103,6 +108,14 @@ inline grpc::string CapitalizeFirstLetter(grpc::string s) {
return s; return s;
} }
inline grpc::string LowercaseFirstLetter(grpc::string s) {
if (s.empty()) {
return s;
}
s[0] = ::tolower(s[0]);
return s;
}
inline grpc::string LowerUnderscoreToUpperCamel(grpc::string str) { inline grpc::string LowerUnderscoreToUpperCamel(grpc::string str) {
std::vector<grpc::string> tokens = tokenize(str, "_"); std::vector<grpc::string> tokens = tokenize(str, "_");
grpc::string result = ""; grpc::string result = "";

@ -40,195 +40,200 @@
#include <sstream> #include <sstream>
using ::grpc::protobuf::io::Printer;
using ::grpc::protobuf::MethodDescriptor;
using ::grpc::protobuf::ServiceDescriptor;
using ::std::map;
using ::grpc::string;
namespace grpc_objective_c_generator { namespace grpc_objective_c_generator {
namespace { namespace {
void PrintSimpleBlockSignature(grpc::protobuf::io::Printer *printer, void PrintProtoRpcDeclarationAsPragma(Printer *printer,
const grpc::protobuf::MethodDescriptor *method, const MethodDescriptor *method,
std::map<grpc::string, grpc::string> *vars) { map<string, string> vars) {
(*vars)["method_name"] = method->name(); vars["client_stream"] = method->client_streaming() ? "stream " : "";
(*vars)["request_type"] = PrefixedName(method->input_type()->name()); vars["server_stream"] = method->server_streaming() ? "stream " : "";
(*vars)["response_type"] = PrefixedName(method->output_type()->name());
if (method->server_streaming()) { printer->Print(vars,
printer->Print("// When the response stream finishes, the handler is " "#pragma mark $method_name$($client_stream$$request_type$)"
"called with nil for both arguments.\n\n"); " returns ($server_stream$$response_type$)\n\n");
} else {
printer->Print("// The handler is only called once.\n\n");
}
printer->Print(*vars, "- (id<GRXLiveSource>)$method_name$WithRequest:"
"($request_type$)request completionHandler:(void(^)"
"($response_type$ *, NSError *))handler");
} }
void PrintSimpleDelegateSignature(grpc::protobuf::io::Printer *printer, void PrintMethodSignature(Printer *printer,
const grpc::protobuf::MethodDescriptor *method, const MethodDescriptor *method,
std::map<grpc::string, grpc::string> *vars) { const map<string, string>& vars) {
(*vars)["method_name"] = method->name(); // TODO(jcanizales): Print method comments.
(*vars)["request_type"] = PrefixedName(method->input_type()->name());
printer->Print(*vars, "- (id<GRXLiveSource>)$method_name$WithRequest:" printer->Print(vars, "- ($return_type$)$method_name$With");
"($request_type$)request delegate:(id<GRXSink>)delegate"); if (method->client_streaming()) {
} printer->Print("RequestsWriter:(id<GRXWriter>)request");
} else {
printer->Print(vars, "Request:($prefix$$request_type$ *)request");
}
void PrintAdvancedSignature(grpc::protobuf::io::Printer *printer, // TODO(jcanizales): Put this on a new line and align colons.
const grpc::protobuf::MethodDescriptor *method, // TODO(jcanizales): eventHandler for server streaming?
std::map<grpc::string, grpc::string> *vars) { printer->Print(" handler:(void(^)(");
(*vars)["method_name"] = method->name(); if (method->server_streaming()) {
printer->Print(*vars, "- (GRXSource *)$method_name$WithRequest:" printer->Print("BOOL done, ");
"(id<GRXSource>)request"); }
printer->Print(vars,
"$prefix$$response_type$ *response, NSError *error))handler");
} }
void PrintSourceMethodSimpleBlock(grpc::protobuf::io::Printer *printer, void PrintSimpleSignature(Printer *printer,
const grpc::protobuf::MethodDescriptor *method, const MethodDescriptor *method,
std::map<grpc::string, grpc::string> *vars) { map<string, string> vars) {
PrintSimpleBlockSignature(printer, method, vars); vars["method_name"] =
grpc_generator::LowercaseFirstLetter(vars["method_name"]);
(*vars)["method_name"] = method->name(); vars["return_type"] = "void";
printer->Print(" {\n"); PrintMethodSignature(printer, method, vars);
printer->Indent();
printer->Print(*vars, "return [[self $method_name$WithRequest:request] "
"connectHandler:^(id value, NSError *error) {\n");
printer->Indent();
printer->Print("handler(value, error);\n");
printer->Outdent();
printer->Print("}];\n");
printer->Outdent();
printer->Print("}\n");
} }
void PrintSourceMethodSimpleDelegate(grpc::protobuf::io::Printer *printer, void PrintAdvancedSignature(Printer *printer,
const grpc::protobuf::MethodDescriptor *method, const MethodDescriptor *method,
std::map<grpc::string, grpc::string> *vars) { map<string, string> vars) {
PrintSimpleDelegateSignature(printer, method, vars); vars["method_name"] = "RPCTo" + vars["method_name"];
vars["return_type"] = "ProtoRPC *";
(*vars)["method_name"] = method->name(); PrintMethodSignature(printer, method, vars);
printer->Print(" {\n");
printer->Indent();
printer->Print(*vars, "return [[self $method_name$WithRequest:request]"
"connectToSink:delegate];\n");
printer->Outdent();
printer->Print("}\n");
} }
void PrintSourceMethodAdvanced(grpc::protobuf::io::Printer *printer, void PrintMethodDeclarations(Printer *printer,
const grpc::protobuf::MethodDescriptor *method, const MethodDescriptor *method,
std::map<grpc::string, grpc::string> *vars) { map<string, string> vars) {
vars["method_name"] = method->name();
vars["request_type"] = method->input_type()->name();
vars["response_type"] = method->output_type()->name();
PrintProtoRpcDeclarationAsPragma(printer, method, vars);
PrintSimpleSignature(printer, method, vars);
printer->Print(";\n\n");
PrintAdvancedSignature(printer, method, vars); PrintAdvancedSignature(printer, method, vars);
printer->Print(";\n\n\n");
}
(*vars)["method_name"] = method->name(); void PrintSimpleImplementation(Printer *printer,
printer->Print(" {\n"); const MethodDescriptor *method,
printer->Indent(); map<string, string> vars) {
printer->Print(*vars, "return [self $method_name$WithRequest:request " printer->Print("{\n");
"client:[self newClient]];\n"); printer->Print(vars, " [[self RPCTo$method_name$With");
printer->Outdent(); if (method->client_streaming()) {
printer->Print("RequestsWriter:request");
} else {
printer->Print("Request:request");
}
printer->Print(" handler:handler] start];\n");
printer->Print("}\n"); printer->Print("}\n");
} }
void PrintSourceMethodHandler(grpc::protobuf::io::Printer *printer, void PrintAdvancedImplementation(Printer *printer,
const grpc::protobuf::MethodDescriptor *method, const MethodDescriptor *method,
std::map<grpc::string, grpc::string> *vars) { map<string, string> vars) {
(*vars)["method_name"] = method->name(); printer->Print("{\n");
(*vars)["response_type"] = PrefixedName(method->output_type()->name()); printer->Print(vars, " return [self RPCToMethod:@\"$method_name$\"\n");
(*vars)["caps_name"] = grpc_generator::CapitalizeFirstLetter(method->name());
printer->Print(" requestsWriter:");
printer->Print(*vars, "- (GRXSource *)$method_name$WithRequest:" if (method->client_streaming()) {
"(id<GRXSource>)request client:(PBgRPCClient *)client {\n"); printer->Print("request\n");
printer->Indent(); } else {
printer->Print(*vars, printer->Print("[GRXWriter writerWithValue:request]\n");
"return [self responseWithMethod:$@\"$caps_name\"\n"); }
printer->Print(*vars,
" class:[$response_type$ class]\n"); printer->Print(vars,
printer->Print(" request:request\n"); " responseClass:[$prefix$$response_type$ class]\n");
printer->Print(" client:client];\n");
printer->Outdent(); printer->Print(" responsesWriteable:[GRXWriteable ");
if (method->server_streaming()) {
printer->Print("writeableWithStreamHandler:handler]];\n");
} else {
printer->Print("writeableWithSingleValueHandler:handler]];\n");
}
printer->Print("}\n"); printer->Print("}\n");
} }
void PrintMethodImplementations(Printer *printer,
const MethodDescriptor *method,
map<string, string> vars) {
vars["method_name"] = method->name();
vars["request_type"] = method->input_type()->name();
vars["response_type"] = method->output_type()->name();
PrintProtoRpcDeclarationAsPragma(printer, method, vars);
// TODO(jcanizales): Print documentation from the method.
PrintSimpleSignature(printer, method, vars);
PrintSimpleImplementation(printer, method, vars);
printer->Print("// Returns a not-yet-started RPC object.\n");
PrintAdvancedSignature(printer, method, vars);
PrintAdvancedImplementation(printer, method, vars);
} }
grpc::string GetHeader(const grpc::protobuf::ServiceDescriptor *service, } // namespace
const grpc::string message_header) {
grpc::string output; string GetHeader(const ServiceDescriptor *service, const string prefix) {
string output;
grpc::protobuf::io::StringOutputStream output_stream(&output); grpc::protobuf::io::StringOutputStream output_stream(&output);
grpc::protobuf::io::Printer printer(&output_stream, '$'); Printer printer(&output_stream, '$');
std::map<grpc::string, grpc::string> vars;
printer.Print("#import \"PBgRPCClient.h\"\n"); printer.Print("@protocol GRXWriteable;\n");
printer.Print("#import \"PBStub.h\"\n"); printer.Print("@protocol GRXWriter;\n\n");
vars["message_header"] = message_header;
printer.Print(vars, "#import \"$message_header$\"\n\n"); map<string, string> vars = {{"service_name", service->name()},
printer.Print("@protocol GRXSource\n"); {"prefix", prefix}};
printer.Print("@class GRXSource\n\n"); printer.Print(vars, "@protocol $prefix$$service_name$ <NSObject>\n\n");
vars["service_name"] = service->name();
printer.Print("@protocol $service_name$Stub <NSObject>\n\n");
printer.Print("#pragma mark Simple block handlers\n\n");
for (int i = 0; i < service->method_count(); i++) {
PrintSimpleBlockSignature(&printer, service->method(i), &vars);
printer.Print(";\n");
}
printer.Print("\n");
printer.Print("#pragma mark Simple delegate handlers.\n\n");
printer.Print("# TODO(jcanizales): Use high-level snippets to remove this duplication.");
for (int i = 0; i < service->method_count(); i++) {
PrintSimpleDelegateSignature(&printer, service->method(i), &vars);
printer.Print(";\n");
}
printer.Print("\n");
printer.Print("#pragma mark Advanced handlers.\n\n");
for (int i = 0; i < service->method_count(); i++) { for (int i = 0; i < service->method_count(); i++) {
PrintAdvancedSignature(&printer, service->method(i), &vars); PrintMethodDeclarations(&printer, service->method(i), vars);
printer.Print(";\n");
} }
printer.Print("\n");
printer.Print("@end\n\n"); printer.Print("@end\n\n");
printer.Print("// Basic stub that only does marshalling and parsing\n");
printer.Print(vars, "@interface $service_name$Stub :" printer.Print("// Basic service implementation, over gRPC, that only does"
" PBStub<$service_name$Stub>\n"); " marshalling and parsing.\n");
printer.Print("- (instancetype)initWithHost:(NSString *)host;\n"); printer.Print(vars, "@interface $prefix$$service_name$ :"
" ProtoService<$prefix$$service_name$>\n");
printer.Print("- (instancetype)initWithHost:(NSString *)host"
" NS_DESIGNATED_INITIALIZER;\n");
printer.Print("@end\n"); printer.Print("@end\n");
return output; return output;
} }
grpc::string GetSource(const grpc::protobuf::ServiceDescriptor *service) { string GetSource(const ServiceDescriptor *service, const string prefix) {
grpc::string output; string output;
grpc::protobuf::io::StringOutputStream output_stream(&output); grpc::protobuf::io::StringOutputStream output_stream(&output);
grpc::protobuf::io::Printer printer(&output_stream, '$'); Printer printer(&output_stream, '$');
std::map<grpc::string, grpc::string> vars;
vars["service_name"] = service->name(); map<string, string> vars = {{"service_name", service->name()},
printer.Print(vars, "#import \"$service_name$Stub.pb.h\"\n"); {"package", service->file()->package()},
printer.Print("#import \"PBGeneratedMessage+GRXSource.h\"\n\n"); {"prefix", prefix}};
vars["full_name"] = service->full_name();
printer.Print(vars,
"static NSString *const kPackageName = @\"$package$\";\n");
printer.Print(vars, printer.Print(vars,
"static NSString *const kInterface = @\"$full_name$\";\n"); "static NSString *const kServiceName = @\"$service_name$\";\n\n");
printer.Print("@implementation $service_name$Stub\n\n");
printer.Print(vars, "@implementation $prefix$$service_name$\n\n");
printer.Print("// Designated initializer\n");
printer.Print("- (instancetype)initWithHost:(NSString *)host {\n"); printer.Print("- (instancetype)initWithHost:(NSString *)host {\n");
printer.Indent(); printer.Print(" return (self = [super initWithHost:host"
printer.Print("if ((self = [super initWithHost:host " " packageName:kPackageName serviceName:kServiceName]);\n");
"interface:kInterface])) {\n");
printer.Print("}\n");
printer.Print("return self;\n");
printer.Outdent();
printer.Print("}\n\n"); printer.Print("}\n\n");
printer.Print("#pragma mark Simple block handlers.\n"); printer.Print("// Override superclass initializer to disallow different"
for (int i = 0; i < service->method_count(); i++) { " package and service names.\n");
PrintSourceMethodSimpleBlock(&printer, service->method(i), &vars); printer.Print("- (instancetype)initWithHost:(NSString *)host\n");
} printer.Print(" packageName:(NSString *)packageName\n");
printer.Print("\n"); printer.Print(" serviceName:(NSString *)serviceName {\n");
printer.Print("#pragma mark Simple delegate handlers.\n"); printer.Print(" return [self initWithHost:host];\n");
for (int i = 0; i < service->method_count(); i++) { printer.Print("}\n\n\n");
PrintSourceMethodSimpleDelegate(&printer, service->method(i), &vars);
}
printer.Print("\n");
printer.Print("#pragma mark Advanced handlers.\n");
for (int i = 0; i < service->method_count(); i++) {
PrintSourceMethodAdvanced(&printer, service->method(i), &vars);
}
printer.Print("\n");
printer.Print("#pragma mark Handlers for subclasses "
"(stub wrappers) to override.\n");
for (int i = 0; i < service->method_count(); i++) { for (int i = 0; i < service->method_count(); i++) {
PrintSourceMethodHandler(&printer, service->method(i), &vars); PrintMethodImplementations(&printer, service->method(i), vars);
} }
printer.Print("@end\n"); printer.Print("@end\n");
return output; return output;
} }

@ -38,10 +38,15 @@
namespace grpc_objective_c_generator { namespace grpc_objective_c_generator {
// Returns the content to be included in the "global_scope" insertion point of
// the generated header file.
grpc::string GetHeader(const grpc::protobuf::ServiceDescriptor *service, grpc::string GetHeader(const grpc::protobuf::ServiceDescriptor *service,
const grpc::string message_header); const grpc::string prefix);
grpc::string GetSource(const grpc::protobuf::ServiceDescriptor *service); // Returns the content to be included in the "global_scope" insertion point of
// the generated implementation file.
grpc::string GetSource(const grpc::protobuf::ServiceDescriptor *service,
const grpc::string prefix);
} // namespace grpc_objective_c_generator } // namespace grpc_objective_c_generator

@ -40,18 +40,8 @@
namespace grpc_objective_c_generator { namespace grpc_objective_c_generator {
const grpc::string prefix = "PBG";
inline grpc::string MessageHeaderName(const grpc::protobuf::FileDescriptor *file) { inline grpc::string MessageHeaderName(const grpc::protobuf::FileDescriptor *file) {
return grpc_generator::FileNameInUpperCamel(file) + ".pb.h"; return grpc_generator::FileNameInUpperCamel(file) + ".pbobjc.h";
}
inline grpc::string StubFileName(grpc::string service_name) {
return prefix + service_name + "Stub";
}
inline grpc::string PrefixedName(grpc::string name) {
return prefix + name;
} }
} }

@ -39,54 +39,77 @@
#include "src/compiler/objective_c_generator.h" #include "src/compiler/objective_c_generator.h"
#include "src/compiler/objective_c_generator_helpers.h" #include "src/compiler/objective_c_generator_helpers.h"
using ::grpc::string;
class ObjectiveCGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator { class ObjectiveCGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator {
public: public:
ObjectiveCGrpcGenerator() {} ObjectiveCGrpcGenerator() {}
virtual ~ObjectiveCGrpcGenerator() {} virtual ~ObjectiveCGrpcGenerator() {}
virtual bool Generate(const grpc::protobuf::FileDescriptor *file, virtual bool Generate(const grpc::protobuf::FileDescriptor *file,
const grpc::string &parameter, const string &parameter,
grpc::protobuf::compiler::GeneratorContext *context, grpc::protobuf::compiler::GeneratorContext *context,
grpc::string *error) const { string *error) const {
if (file->service_count() == 0) { if (file->service_count() == 0) {
// No services. Do nothing. // No services. Do nothing.
return true; return true;
} }
for (int i = 0; i < file->service_count(); i++) { string file_name = grpc_generator::FileNameInUpperCamel(file);
const grpc::protobuf::ServiceDescriptor *service = file->service(i); string prefix = file->options().objc_class_prefix();
grpc::string file_name = grpc_objective_c_generator::StubFileName(
service->name()); {
// Generate .pbrpc.h
// Generate .pb.h
grpc::string header_code = grpc_objective_c_generator::GetHeader( string imports = string("#import \"") + file_name + ".pbobjc.h\"\n"
service, grpc_objective_c_generator::MessageHeaderName(file)); "#import <gRPC/ProtoService.h>\n";
std::unique_ptr<grpc::protobuf::io::ZeroCopyOutputStream> header_output(
context->Open(file_name + ".pb.h")); // TODO(jcanizales): Instead forward-declare the input and output types
grpc::protobuf::io::CodedOutputStream header_coded_out( // and import the files in the .pbrpc.m
header_output.get()); string proto_imports;
header_coded_out.WriteRaw(header_code.data(), header_code.size()); for (int i = 0; i < file->dependency_count(); i++) {
string header = grpc_objective_c_generator::MessageHeaderName(
// Generate .pb.m file->dependency(i));
grpc::string source_code = grpc_objective_c_generator::GetSource(service); proto_imports += string("#import \"") + header + "\"\n";
std::unique_ptr<grpc::protobuf::io::ZeroCopyOutputStream> source_output( }
context->Open(file_name + ".pb.m"));
grpc::protobuf::io::CodedOutputStream source_coded_out( string declarations;
source_output.get()); for (int i = 0; i < file->service_count(); i++) {
source_coded_out.WriteRaw(source_code.data(), source_code.size()); const grpc::protobuf::ServiceDescriptor *service = file->service(i);
declarations += grpc_objective_c_generator::GetHeader(service, prefix);
}
Write(context, file_name + ".pbrpc.h",
imports + '\n' + proto_imports + '\n' + declarations);
}
{
// Generate .pbrpc.m
string imports = string("#import \"") + file_name + ".pbrpc.h\"\n"
"#import <gRPC/GRXWriteable.h>\n"
"#import <gRPC/GRXWriter+Immediate.h>\n"
"#import <gRPC/ProtoRPC.h>\n";
string definitions;
for (int i = 0; i < file->service_count(); i++) {
const grpc::protobuf::ServiceDescriptor *service = file->service(i);
definitions += grpc_objective_c_generator::GetSource(service, prefix);
}
Write(context, file_name + ".pbrpc.m", imports + '\n' + definitions);
} }
return true; return true;
} }
private: private:
// Insert the given code into the given file at the given insertion point. // Write the given code into the given file.
void Insert(grpc::protobuf::compiler::GeneratorContext *context, void Write(grpc::protobuf::compiler::GeneratorContext *context,
const grpc::string &filename, const grpc::string &insertion_point, const string &filename, const string &code) const {
const grpc::string &code) const {
std::unique_ptr<grpc::protobuf::io::ZeroCopyOutputStream> output( std::unique_ptr<grpc::protobuf::io::ZeroCopyOutputStream> output(
context->OpenForInsert(filename, insertion_point)); context->Open(filename));
grpc::protobuf::io::CodedOutputStream coded_out(output.get()); grpc::protobuf::io::CodedOutputStream coded_out(output.get());
coded_out.WriteRaw(code.data(), code.size()); coded_out.WriteRaw(code.data(), code.size());
} }

@ -67,7 +67,6 @@ static grpc_httpcli_post_override g_post_override = NULL;
static void next_address(internal_request *req); static void next_address(internal_request *req);
static void finish(internal_request *req, int success) { static void finish(internal_request *req, int success) {
gpr_log(GPR_DEBUG, "%s", __FUNCTION__);
req->on_response(req->user_data, success ? &req->parser.r : NULL); req->on_response(req->user_data, success ? &req->parser.r : NULL);
grpc_httpcli_parser_destroy(&req->parser); grpc_httpcli_parser_destroy(&req->parser);
if (req->addresses != NULL) { if (req->addresses != NULL) {
@ -86,8 +85,6 @@ static void on_read(void *user_data, gpr_slice *slices, size_t nslices,
internal_request *req = user_data; internal_request *req = user_data;
size_t i; size_t i;
gpr_log(GPR_DEBUG, "%s nslices=%d status=%d", __FUNCTION__, nslices, status);
for (i = 0; i < nslices; i++) { for (i = 0; i < nslices; i++) {
if (GPR_SLICE_LENGTH(slices[i])) { if (GPR_SLICE_LENGTH(slices[i])) {
req->have_read_byte = 1; req->have_read_byte = 1;
@ -120,13 +117,11 @@ done:
} }
static void on_written(internal_request *req) { static void on_written(internal_request *req) {
gpr_log(GPR_DEBUG, "%s", __FUNCTION__);
grpc_endpoint_notify_on_read(req->ep, on_read, req); grpc_endpoint_notify_on_read(req->ep, on_read, req);
} }
static void done_write(void *arg, grpc_endpoint_cb_status status) { static void done_write(void *arg, grpc_endpoint_cb_status status) {
internal_request *req = arg; internal_request *req = arg;
gpr_log(GPR_DEBUG, "%s", __FUNCTION__);
switch (status) { switch (status) {
case GRPC_ENDPOINT_CB_OK: case GRPC_ENDPOINT_CB_OK:
on_written(req); on_written(req);
@ -141,7 +136,6 @@ static void done_write(void *arg, grpc_endpoint_cb_status status) {
static void start_write(internal_request *req) { static void start_write(internal_request *req) {
gpr_slice_ref(req->request_text); gpr_slice_ref(req->request_text);
gpr_log(GPR_DEBUG, "%s", __FUNCTION__);
switch ( switch (
grpc_endpoint_write(req->ep, &req->request_text, 1, done_write, req)) { grpc_endpoint_write(req->ep, &req->request_text, 1, done_write, req)) {
case GRPC_ENDPOINT_WRITE_DONE: case GRPC_ENDPOINT_WRITE_DONE:
@ -159,7 +153,6 @@ static void on_secure_transport_setup_done(void *rp,
grpc_security_status status, grpc_security_status status,
grpc_endpoint *secure_endpoint) { grpc_endpoint *secure_endpoint) {
internal_request *req = rp; internal_request *req = rp;
gpr_log(GPR_DEBUG, "%s", __FUNCTION__);
if (status != GRPC_SECURITY_OK) { if (status != GRPC_SECURITY_OK) {
gpr_log(GPR_ERROR, "Secure transport setup failed with error %d.", status); gpr_log(GPR_ERROR, "Secure transport setup failed with error %d.", status);
finish(req, 0); finish(req, 0);
@ -172,7 +165,6 @@ static void on_secure_transport_setup_done(void *rp,
static void on_connected(void *arg, grpc_endpoint *tcp) { static void on_connected(void *arg, grpc_endpoint *tcp) {
internal_request *req = arg; internal_request *req = arg;
gpr_log(GPR_DEBUG, "%s", __FUNCTION__);
if (!tcp) { if (!tcp) {
next_address(req); next_address(req);
return; return;
@ -200,7 +192,6 @@ static void on_connected(void *arg, grpc_endpoint *tcp) {
static void next_address(internal_request *req) { static void next_address(internal_request *req) {
grpc_resolved_address *addr; grpc_resolved_address *addr;
gpr_log(GPR_DEBUG, "%s", __FUNCTION__);
if (req->next_address == req->addresses->naddrs) { if (req->next_address == req->addresses->naddrs) {
finish(req, 0); finish(req, 0);
return; return;
@ -212,7 +203,6 @@ static void next_address(internal_request *req) {
static void on_resolved(void *arg, grpc_resolved_addresses *addresses) { static void on_resolved(void *arg, grpc_resolved_addresses *addresses) {
internal_request *req = arg; internal_request *req = arg;
gpr_log(GPR_DEBUG, "%s", __FUNCTION__);
if (!addresses) { if (!addresses) {
finish(req, 0); finish(req, 0);
return; return;

@ -258,7 +258,6 @@ static void unary_poll_do_promote(void *args, int success) {
grpc_pollset *pollset = up_args->pollset; grpc_pollset *pollset = up_args->pollset;
grpc_fd *fd = up_args->fd; grpc_fd *fd = up_args->fd;
int do_shutdown_cb = 0; int do_shutdown_cb = 0;
gpr_free(up_args);
/* /*
* This is quite tricky. There are a number of cases to keep in mind here: * This is quite tricky. There are a number of cases to keep in mind here:
@ -273,8 +272,12 @@ static void unary_poll_do_promote(void *args, int success) {
/* First we need to ensure that nobody is polling concurrently */ /* First we need to ensure that nobody is polling concurrently */
while (pollset->counter != 0) { while (pollset->counter != 0) {
grpc_pollset_kick(pollset); grpc_pollset_kick(pollset);
gpr_cv_wait(&pollset->cv, &pollset->mu, gpr_inf_future); grpc_iomgr_add_callback(unary_poll_do_promote, up_args);
gpr_mu_unlock(&pollset->mu);
return;
} }
gpr_free(up_args);
/* At this point the pollset may no longer be a unary poller. In that case /* At this point the pollset may no longer be a unary poller. In that case
* we should just call the right add function and be done. */ * we should just call the right add function and be done. */
/* TODO(klempner): If we're not careful this could cause infinite recursion. /* TODO(klempner): If we're not careful this could cause infinite recursion.

@ -169,8 +169,7 @@ int grpc_sockaddr_get_port(const struct sockaddr *addr) {
case AF_UNIX: case AF_UNIX:
return 1; return 1;
default: default:
gpr_log(GPR_ERROR, "Unknown socket family %d in %s", addr->sa_family, gpr_log(GPR_ERROR, "Unknown socket family %d in grpc_sockaddr_get_port", addr->sa_family);
__FUNCTION__);
return 0; return 0;
} }
} }
@ -184,8 +183,7 @@ int grpc_sockaddr_set_port(const struct sockaddr *addr, int port) {
((struct sockaddr_in6 *)addr)->sin6_port = htons(port); ((struct sockaddr_in6 *)addr)->sin6_port = htons(port);
return 1; return 1;
default: default:
gpr_log(GPR_ERROR, "Unknown socket family %d in %s", addr->sa_family, gpr_log(GPR_ERROR, "Unknown socket family %d in grpc_sockaddr_set_port", addr->sa_family);
__FUNCTION__);
return 0; return 0;
} }
} }

@ -138,8 +138,10 @@ static void slice_state_remove_prefix(grpc_tcp_slice_state *state,
native "trim the first N bytes" operation to splice */ native "trim the first N bytes" operation to splice */
/* TODO(klempner): This really shouldn't be modifying the current slice /* TODO(klempner): This really shouldn't be modifying the current slice
unless we own the slices array. */ unless we own the slices array. */
*current_slice = gpr_slice_split_tail(current_slice, prefix_bytes); gpr_slice tail;
tail = gpr_slice_split_tail(current_slice, prefix_bytes);
gpr_slice_unref(*current_slice); gpr_slice_unref(*current_slice);
*current_slice = tail;
return; return;
} else { } else {
gpr_slice_unref(*current_slice); gpr_slice_unref(*current_slice);

@ -55,6 +55,7 @@ typedef enum {
typedef struct grpc_timer_entry { typedef struct grpc_timer_entry {
grpc_precise_clock tm; grpc_precise_clock tm;
int tag; int tag;
const char* tagstr;
marker_type type; marker_type type;
void* id; void* id;
const char* file; const char* file;
@ -70,18 +71,19 @@ static void log_report() {
int i; int i;
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
grpc_timer_entry* entry = &(log[i]); grpc_timer_entry* entry = &(log[i]);
printf("GRPC_LAT_PROF " GRPC_PRECISE_CLOCK_FORMAT " %p %c %d %p %s %d\n", printf("GRPC_LAT_PROF " GRPC_PRECISE_CLOCK_FORMAT
" %p %c %d(%s) %p %s %d\n",
GRPC_PRECISE_CLOCK_PRINTF_ARGS(&entry->tm), GRPC_PRECISE_CLOCK_PRINTF_ARGS(&entry->tm),
(void*)(gpr_intptr)gpr_thd_currentid(), entry->type, entry->tag, (void*)(gpr_intptr)gpr_thd_currentid(), entry->type, entry->tag,
entry->id, entry->file, entry->line); entry->tagstr, entry->id, entry->file, entry->line);
} }
/* Now clear out the log */ /* Now clear out the log */
count = 0; count = 0;
} }
static void grpc_timers_log_add(int tag, marker_type type, void* id, static void grpc_timers_log_add(int tag, const char* tagstr, marker_type type,
const char* file, int line) { void* id, const char* file, int line) {
grpc_timer_entry* entry; grpc_timer_entry* entry;
/* TODO (vpai) : Improve concurrency */ /* TODO (vpai) : Improve concurrency */
@ -93,6 +95,7 @@ static void grpc_timers_log_add(int tag, marker_type type, void* id,
grpc_precise_clock_now(&entry->tm); grpc_precise_clock_now(&entry->tm);
entry->tag = tag; entry->tag = tag;
entry->tagstr = tagstr;
entry->type = type; entry->type = type;
entry->id = id; entry->id = id;
entry->file = file; entry->file = file;
@ -100,28 +103,31 @@ static void grpc_timers_log_add(int tag, marker_type type, void* id,
} }
/* Latency profiler API implementation. */ /* Latency profiler API implementation. */
void grpc_timer_add_mark(int tag, void* id, const char* file, int line) { void grpc_timer_add_mark(int tag, const char* tagstr, void* id,
const char* file, int line) {
if (tag < GRPC_PTAG_IGNORE_THRESHOLD) { if (tag < GRPC_PTAG_IGNORE_THRESHOLD) {
grpc_timers_log_add(tag, MARK, id, file, line); grpc_timers_log_add(tag, tagstr, MARK, id, file, line);
} }
} }
void grpc_timer_add_important_mark(int tag, void* id, const char* file, void grpc_timer_add_important_mark(int tag, const char* tagstr, void* id,
int line) { const char* file, int line) {
if (tag < GRPC_PTAG_IGNORE_THRESHOLD) { if (tag < GRPC_PTAG_IGNORE_THRESHOLD) {
grpc_timers_log_add(tag, IMPORTANT, id, file, line); grpc_timers_log_add(tag, tagstr, IMPORTANT, id, file, line);
} }
} }
void grpc_timer_begin(int tag, void* id, const char* file, int line) { void grpc_timer_begin(int tag, const char* tagstr, void* id, const char* file,
int line) {
if (tag < GRPC_PTAG_IGNORE_THRESHOLD) { if (tag < GRPC_PTAG_IGNORE_THRESHOLD) {
grpc_timers_log_add(tag, BEGIN, id, file, line); grpc_timers_log_add(tag, tagstr, BEGIN, id, file, line);
} }
} }
void grpc_timer_end(int tag, void* id, const char* file, int line) { void grpc_timer_end(int tag, const char* tagstr, void* id, const char* file,
int line) {
if (tag < GRPC_PTAG_IGNORE_THRESHOLD) { if (tag < GRPC_PTAG_IGNORE_THRESHOLD) {
grpc_timers_log_add(tag, END, id, file, line); grpc_timers_log_add(tag, tagstr, END, id, file, line);
} }
} }

@ -42,20 +42,23 @@
#include "src/core/profiling/stap_probes.h" #include "src/core/profiling/stap_probes.h"
/* Latency profiler API implementation. */ /* Latency profiler API implementation. */
void grpc_timer_add_mark(int tag, void* id, const char* file, int line) { void grpc_timer_add_mark(int tag, const char* tagstr, void* id,
const char* file, int line) {
_STAP_ADD_MARK(tag); _STAP_ADD_MARK(tag);
} }
void grpc_timer_add_important_mark(int tag, void* id, const char* file, void grpc_timer_add_important_mark(int tag, const char* tagstr, void* id,
int line) { const char* file, int line) {
_STAP_ADD_IMPORTANT_MARK(tag); _STAP_ADD_IMPORTANT_MARK(tag);
} }
void grpc_timer_begin(int tag, void* id, const char* file, int line) { void grpc_timer_begin(int tag, const char* tagstr, void* id, const char* file,
int line) {
_STAP_TIMING_NS_BEGIN(tag); _STAP_TIMING_NS_BEGIN(tag);
} }
void grpc_timer_end(int tag, void* id, const char* file, int line) { void grpc_timer_end(int tag, const char* tagstr, void* id, const char* file,
int line) {
_STAP_TIMING_NS_END(tag); _STAP_TIMING_NS_END(tag);
} }

@ -41,11 +41,14 @@ extern "C" {
void grpc_timers_global_init(void); void grpc_timers_global_init(void);
void grpc_timers_global_destroy(void); void grpc_timers_global_destroy(void);
void grpc_timer_add_mark(int tag, void *id, const char *file, int line); void grpc_timer_add_mark(int tag, const char *tagstr, void *id,
void grpc_timer_add_important_mark(int tag, void *id, const char *file, const char *file, int line);
int line); void grpc_timer_add_important_mark(int tag, const char *tagstr, void *id,
void grpc_timer_begin(int tag, void *id, const char *file, int line); const char *file, int line);
void grpc_timer_end(int tag, void *id, const char *file, int line); void grpc_timer_begin(int tag, const char *tagstr, void *id, const char *file,
int line);
void grpc_timer_end(int tag, const char *tagstr, void *id, const char *file,
int line);
enum grpc_profiling_tags { enum grpc_profiling_tags {
/* Any GRPC_PTAG_* >= than the threshold won't generate any profiling mark. */ /* Any GRPC_PTAG_* >= than the threshold won't generate any profiling mark. */
@ -103,25 +106,27 @@ enum grpc_profiling_tags {
#endif #endif
/* Generic profiling interface. */ /* Generic profiling interface. */
#define GRPC_TIMER_MARK(tag, id) \ #define GRPC_TIMER_MARK(tag, id) \
if (tag < GRPC_PTAG_IGNORE_THRESHOLD) { \ if (tag < GRPC_PTAG_IGNORE_THRESHOLD) { \
grpc_timer_add_mark(tag, ((void *)(gpr_intptr)(id)), __FILE__, __LINE__); \ grpc_timer_add_mark(tag, #tag, ((void *)(gpr_intptr)(id)), __FILE__, \
__LINE__); \
} }
#define GRPC_TIMER_IMPORTANT_MARK(tag, id) \ #define GRPC_TIMER_IMPORTANT_MARK(tag, id) \
if (tag < GRPC_PTAG_IGNORE_THRESHOLD) { \ if (tag < GRPC_PTAG_IGNORE_THRESHOLD) { \
grpc_timer_add_important_mark(tag, ((void *)(gpr_intptr)(id)), __FILE__, \ grpc_timer_add_important_mark(tag, #tag, ((void *)(gpr_intptr)(id)), \
__LINE__); \ __FILE__, __LINE__); \
} }
#define GRPC_TIMER_BEGIN(tag, id) \ #define GRPC_TIMER_BEGIN(tag, id) \
if (tag < GRPC_PTAG_IGNORE_THRESHOLD) { \ if (tag < GRPC_PTAG_IGNORE_THRESHOLD) { \
grpc_timer_begin(tag, ((void *)(gpr_intptr)(id)), __FILE__, __LINE__); \ grpc_timer_begin(tag, #tag, ((void *)(gpr_intptr)(id)), __FILE__, \
__LINE__); \
} }
#define GRPC_TIMER_END(tag, id) \ #define GRPC_TIMER_END(tag, id) \
if (tag < GRPC_PTAG_IGNORE_THRESHOLD) { \ if (tag < GRPC_PTAG_IGNORE_THRESHOLD) { \
grpc_timer_end(tag, ((void *)(gpr_intptr)(id)), __FILE__, __LINE__); \ grpc_timer_end(tag, #tag, ((void *)(gpr_intptr)(id)), __FILE__, __LINE__); \
} }
#ifdef GRPC_STAP_PROFILER #ifdef GRPC_STAP_PROFILER

@ -82,7 +82,7 @@ static const char *ssl_cipher_suites(void) {
/* -- Common methods. -- */ /* -- Common methods. -- */
/* Returns the first property with that name. */ /* Returns the first property with that name. */
static const tsi_peer_property *tsi_peer_get_property_by_name( const tsi_peer_property *tsi_peer_get_property_by_name(
const tsi_peer *peer, const char *name) { const tsi_peer *peer, const char *name) {
size_t i; size_t i;
if (peer == NULL) return NULL; if (peer == NULL) return NULL;

@ -198,4 +198,8 @@ typedef struct {
grpc_security_status grpc_ssl_server_security_connector_create( grpc_security_status grpc_ssl_server_security_connector_create(
const grpc_ssl_server_config *config, grpc_security_connector **sc); const grpc_ssl_server_config *config, grpc_security_connector **sc);
/* Util. */
const tsi_peer_property *tsi_peer_get_property_by_name(
const tsi_peer *peer, const char *name);
#endif /* GRPC_INTERNAL_CORE_SECURITY_SECURITY_CONNECTOR_H */ #endif /* GRPC_INTERNAL_CORE_SECURITY_SECURITY_CONNECTOR_H */

@ -55,9 +55,9 @@ struct gpr_subprocess {
int joined; int joined;
}; };
char *gpr_subprocess_binary_extension() { return ""; } const char *gpr_subprocess_binary_extension() { return ""; }
gpr_subprocess *gpr_subprocess_create(int argc, char **argv) { gpr_subprocess *gpr_subprocess_create(int argc, const char **argv) {
gpr_subprocess *r; gpr_subprocess *r;
int pid; int pid;
char **exec_args; char **exec_args;
@ -92,7 +92,11 @@ void gpr_subprocess_destroy(gpr_subprocess *p) {
int gpr_subprocess_join(gpr_subprocess *p) { int gpr_subprocess_join(gpr_subprocess *p) {
int status; int status;
retry:
if (waitpid(p->pid, &status, 0) == -1) { if (waitpid(p->pid, &status, 0) == -1) {
if (errno == EINTR) {
goto retry;
}
gpr_log(GPR_ERROR, "waitpid failed: %s", strerror(errno)); gpr_log(GPR_ERROR, "waitpid failed: %s", strerror(errno));
return -1; return -1;
} }

@ -401,6 +401,7 @@ static int is_op_live(grpc_call *call, grpc_ioreq_op op) {
static void lock(grpc_call *call) { gpr_mu_lock(&call->mu); } static void lock(grpc_call *call) { gpr_mu_lock(&call->mu); }
static int need_more_data(grpc_call *call) { static int need_more_data(grpc_call *call) {
if (call->read_state == READ_STATE_STREAM_CLOSED) return 0;
return is_op_live(call, GRPC_IOREQ_RECV_INITIAL_METADATA) || return is_op_live(call, GRPC_IOREQ_RECV_INITIAL_METADATA) ||
(is_op_live(call, GRPC_IOREQ_RECV_MESSAGE) && grpc_bbq_empty(&call->incoming_queue)) || (is_op_live(call, GRPC_IOREQ_RECV_MESSAGE) && grpc_bbq_empty(&call->incoming_queue)) ||
is_op_live(call, GRPC_IOREQ_RECV_TRAILING_METADATA) || is_op_live(call, GRPC_IOREQ_RECV_TRAILING_METADATA) ||
@ -408,8 +409,7 @@ static int need_more_data(grpc_call *call) {
is_op_live(call, GRPC_IOREQ_RECV_STATUS_DETAILS) || is_op_live(call, GRPC_IOREQ_RECV_STATUS_DETAILS) ||
(is_op_live(call, GRPC_IOREQ_RECV_CLOSE) && (is_op_live(call, GRPC_IOREQ_RECV_CLOSE) &&
grpc_bbq_empty(&call->incoming_queue)) || grpc_bbq_empty(&call->incoming_queue)) ||
(call->write_state == WRITE_STATE_INITIAL && !call->is_client && (call->write_state == WRITE_STATE_INITIAL && !call->is_client);
call->read_state < READ_STATE_GOT_INITIAL_METADATA);
} }
static void unlock(grpc_call *call) { static void unlock(grpc_call *call) {
@ -536,9 +536,8 @@ static void finish_live_ioreq_op(grpc_call *call, grpc_ioreq_op op,
switch ((grpc_ioreq_op)i) { switch ((grpc_ioreq_op)i) {
case GRPC_IOREQ_RECV_MESSAGE: case GRPC_IOREQ_RECV_MESSAGE:
case GRPC_IOREQ_SEND_MESSAGE: case GRPC_IOREQ_SEND_MESSAGE:
if (master->success) { call->request_set[i] = REQSET_EMPTY;
call->request_set[i] = REQSET_EMPTY; if (!master->success) {
} else {
call->write_state = WRITE_STATE_WRITE_CLOSED; call->write_state = WRITE_STATE_WRITE_CLOSED;
} }
break; break;
@ -583,11 +582,29 @@ static void finish_ioreq_op(grpc_call *call, grpc_ioreq_op op, int success) {
} }
} }
static void early_out_write_ops(grpc_call *call) {
switch (call->write_state) {
case WRITE_STATE_WRITE_CLOSED:
finish_ioreq_op(call, GRPC_IOREQ_SEND_MESSAGE, 0);
finish_ioreq_op(call, GRPC_IOREQ_SEND_STATUS, 0);
finish_ioreq_op(call, GRPC_IOREQ_SEND_TRAILING_METADATA, 0);
finish_ioreq_op(call, GRPC_IOREQ_SEND_CLOSE, 1);
/* fallthrough */
case WRITE_STATE_STARTED:
finish_ioreq_op(call, GRPC_IOREQ_SEND_INITIAL_METADATA, 0);
/* fallthrough */
case WRITE_STATE_INITIAL:
/* do nothing */
break;
}
}
static void call_on_done_send(void *pc, int success) { static void call_on_done_send(void *pc, int success) {
grpc_call *call = pc; grpc_call *call = pc;
lock(call); lock(call);
if (call->last_send_contains & (1 << GRPC_IOREQ_SEND_INITIAL_METADATA)) { if (call->last_send_contains & (1 << GRPC_IOREQ_SEND_INITIAL_METADATA)) {
finish_ioreq_op(call, GRPC_IOREQ_SEND_INITIAL_METADATA, success); finish_ioreq_op(call, GRPC_IOREQ_SEND_INITIAL_METADATA, success);
call->write_state = WRITE_STATE_STARTED;
} }
if (call->last_send_contains & (1 << GRPC_IOREQ_SEND_MESSAGE)) { if (call->last_send_contains & (1 << GRPC_IOREQ_SEND_MESSAGE)) {
finish_ioreq_op(call, GRPC_IOREQ_SEND_MESSAGE, success); finish_ioreq_op(call, GRPC_IOREQ_SEND_MESSAGE, success);
@ -596,7 +613,13 @@ static void call_on_done_send(void *pc, int success) {
finish_ioreq_op(call, GRPC_IOREQ_SEND_TRAILING_METADATA, success); finish_ioreq_op(call, GRPC_IOREQ_SEND_TRAILING_METADATA, success);
finish_ioreq_op(call, GRPC_IOREQ_SEND_STATUS, success); finish_ioreq_op(call, GRPC_IOREQ_SEND_STATUS, success);
finish_ioreq_op(call, GRPC_IOREQ_SEND_CLOSE, 1); finish_ioreq_op(call, GRPC_IOREQ_SEND_CLOSE, 1);
call->write_state = WRITE_STATE_WRITE_CLOSED;
}
if (!success) {
call->write_state = WRITE_STATE_WRITE_CLOSED;
early_out_write_ops(call);
} }
call->send_ops.nops = 0;
call->last_send_contains = 0; call->last_send_contains = 0;
call->sending = 0; call->sending = 0;
unlock(call); unlock(call);
@ -810,7 +833,6 @@ static int fill_send_ops(grpc_call *call, grpc_transport_op *op) {
op->send_ops = &call->send_ops; op->send_ops = &call->send_ops;
op->bind_pollset = grpc_cq_pollset(call->cq); op->bind_pollset = grpc_cq_pollset(call->cq);
call->last_send_contains |= 1 << GRPC_IOREQ_SEND_INITIAL_METADATA; call->last_send_contains |= 1 << GRPC_IOREQ_SEND_INITIAL_METADATA;
call->write_state = WRITE_STATE_STARTED;
call->send_initial_metadata_count = 0; call->send_initial_metadata_count = 0;
/* fall through intended */ /* fall through intended */
case WRITE_STATE_STARTED: case WRITE_STATE_STARTED:
@ -826,7 +848,6 @@ static int fill_send_ops(grpc_call *call, grpc_transport_op *op) {
op->is_last_send = 1; op->is_last_send = 1;
op->send_ops = &call->send_ops; op->send_ops = &call->send_ops;
call->last_send_contains |= 1 << GRPC_IOREQ_SEND_CLOSE; call->last_send_contains |= 1 << GRPC_IOREQ_SEND_CLOSE;
call->write_state = WRITE_STATE_WRITE_CLOSED;
if (!call->is_client) { if (!call->is_client) {
/* send trailing metadata */ /* send trailing metadata */
data = call->request_data[GRPC_IOREQ_SEND_TRAILING_METADATA]; data = call->request_data[GRPC_IOREQ_SEND_TRAILING_METADATA];
@ -918,23 +939,6 @@ static void finish_read_ops(grpc_call *call) {
} }
} }
static void early_out_write_ops(grpc_call *call) {
switch (call->write_state) {
case WRITE_STATE_WRITE_CLOSED:
finish_ioreq_op(call, GRPC_IOREQ_SEND_MESSAGE, 0);
finish_ioreq_op(call, GRPC_IOREQ_SEND_STATUS, 0);
finish_ioreq_op(call, GRPC_IOREQ_SEND_TRAILING_METADATA, 0);
finish_ioreq_op(call, GRPC_IOREQ_SEND_CLOSE, 1);
/* fallthrough */
case WRITE_STATE_STARTED:
finish_ioreq_op(call, GRPC_IOREQ_SEND_INITIAL_METADATA, 0);
/* fallthrough */
case WRITE_STATE_INITIAL:
/* do nothing */
break;
}
}
static grpc_call_error start_ioreq(grpc_call *call, const grpc_ioreq *reqs, static grpc_call_error start_ioreq(grpc_call *call, const grpc_ioreq *reqs,
size_t nreqs, size_t nreqs,
grpc_ioreq_completion_func completion, grpc_ioreq_completion_func completion,
@ -1175,6 +1179,10 @@ static void set_cancelled_value(grpc_status_code status, void *dest) {
} }
static void finish_batch(grpc_call *call, int success, void *tag) { static void finish_batch(grpc_call *call, int success, void *tag) {
grpc_cq_end_op(call->cq, tag, call, success);
}
static void finish_batch_with_close(grpc_call *call, int success, void *tag) {
grpc_cq_end_op(call->cq, tag, call, 1); grpc_cq_end_op(call->cq, tag, call, 1);
} }
@ -1185,6 +1193,7 @@ grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops,
size_t out; size_t out;
const grpc_op *op; const grpc_op *op;
grpc_ioreq *req; grpc_ioreq *req;
void (*finish_func)(grpc_call *, int, void *) = finish_batch;
GRPC_CALL_LOG_BATCH(GPR_INFO, call, ops, nops, tag); GRPC_CALL_LOG_BATCH(GPR_INFO, call, ops, nops, tag);
@ -1268,6 +1277,7 @@ grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops,
op->data.recv_status_on_client.trailing_metadata; op->data.recv_status_on_client.trailing_metadata;
req = &reqs[out++]; req = &reqs[out++];
req->op = GRPC_IOREQ_RECV_CLOSE; req->op = GRPC_IOREQ_RECV_CLOSE;
finish_func = finish_batch_with_close;
break; break;
case GRPC_OP_RECV_CLOSE_ON_SERVER: case GRPC_OP_RECV_CLOSE_ON_SERVER:
req = &reqs[out++]; req = &reqs[out++];
@ -1277,13 +1287,14 @@ grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops,
op->data.recv_close_on_server.cancelled; op->data.recv_close_on_server.cancelled;
req = &reqs[out++]; req = &reqs[out++];
req->op = GRPC_IOREQ_RECV_CLOSE; req->op = GRPC_IOREQ_RECV_CLOSE;
finish_func = finish_batch_with_close;
break; break;
} }
} }
grpc_cq_begin_op(call->cq, call); grpc_cq_begin_op(call->cq, call);
return grpc_call_start_ioreq_and_call_back(call, reqs, out, finish_batch, return grpc_call_start_ioreq_and_call_back(call, reqs, out, finish_func,
tag); tag);
} }

@ -275,14 +275,14 @@ grpc_event grpc_completion_queue_pluck(grpc_completion_queue *cc, void *tag,
gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset));
memset(&ret, 0, sizeof(ret)); memset(&ret, 0, sizeof(ret));
ret.type = GRPC_QUEUE_TIMEOUT; ret.type = GRPC_QUEUE_TIMEOUT;
GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ev->base); GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ret);
return ret; return ret;
} }
} }
gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset));
ret = ev->base; ret = ev->base;
gpr_free(ev); gpr_free(ev);
GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ev->base); GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ret);
return ret; return ret;
} }

@ -427,6 +427,8 @@ static void server_on_recv(void *ptr, int success) {
grpc_iomgr_add_callback(kill_zombie, elem); grpc_iomgr_add_callback(kill_zombie, elem);
} else if (calld->state == PENDING) { } else if (calld->state == PENDING) {
call_list_remove(calld, PENDING_START); call_list_remove(calld, PENDING_START);
calld->state = ZOMBIED;
grpc_iomgr_add_callback(kill_zombie, elem);
} }
gpr_mu_unlock(&chand->server->mu); gpr_mu_unlock(&chand->server->mu);
break; break;
@ -663,7 +665,7 @@ void *grpc_server_register_method(grpc_server *server, const char *method,
const char *host) { const char *host) {
registered_method *m; registered_method *m;
if (!method) { if (!method) {
gpr_log(GPR_ERROR, "%s method string cannot be NULL", __FUNCTION__); gpr_log(GPR_ERROR, "grpc_server_register_method method string cannot be NULL");
return NULL; return NULL;
} }
for (m = server->registered_methods; m; m = m->next) { for (m = server->registered_methods; m; m = m->next) {

@ -53,12 +53,14 @@ typedef struct {
gpr_uint8 send_ping_ack; gpr_uint8 send_ping_ack;
gpr_uint8 process_ping_reply; gpr_uint8 process_ping_reply;
gpr_uint8 goaway; gpr_uint8 goaway;
gpr_uint8 rst_stream;
gpr_int64 initial_window_update; gpr_int64 initial_window_update;
gpr_uint32 window_update; gpr_uint32 window_update;
gpr_uint32 goaway_last_stream_index; gpr_uint32 goaway_last_stream_index;
gpr_uint32 goaway_error; gpr_uint32 goaway_error;
gpr_slice goaway_text; gpr_slice goaway_text;
gpr_uint32 rst_stream_reason;
} grpc_chttp2_parse_state; } grpc_chttp2_parse_state;
#define GRPC_CHTTP2_FRAME_DATA 0 #define GRPC_CHTTP2_FRAME_DATA 0

@ -32,6 +32,9 @@
*/ */
#include "src/core/transport/chttp2/frame_rst_stream.h" #include "src/core/transport/chttp2/frame_rst_stream.h"
#include <grpc/support/log.h>
#include "src/core/transport/chttp2/frame.h" #include "src/core/transport/chttp2/frame.h"
gpr_slice grpc_chttp2_rst_stream_create(gpr_uint32 id, gpr_uint32 code) { gpr_slice grpc_chttp2_rst_stream_create(gpr_uint32 id, gpr_uint32 code) {
@ -54,3 +57,40 @@ gpr_slice grpc_chttp2_rst_stream_create(gpr_uint32 id, gpr_uint32 code) {
return slice; return slice;
} }
grpc_chttp2_parse_error grpc_chttp2_rst_stream_parser_begin_frame(
grpc_chttp2_rst_stream_parser *parser, gpr_uint32 length, gpr_uint8 flags) {
if (length != 4) {
gpr_log(GPR_ERROR, "invalid rst_stream: length=%d, flags=%02x", length, flags);
return GRPC_CHTTP2_CONNECTION_ERROR;
}
parser->byte = 0;
return GRPC_CHTTP2_PARSE_OK;
}
grpc_chttp2_parse_error grpc_chttp2_rst_stream_parser_parse(
void *parser, grpc_chttp2_parse_state *state, gpr_slice slice,
int is_last) {
gpr_uint8 *const beg = GPR_SLICE_START_PTR(slice);
gpr_uint8 *const end = GPR_SLICE_END_PTR(slice);
gpr_uint8 *cur = beg;
grpc_chttp2_rst_stream_parser *p = parser;
while (p->byte != 4 && cur != end) {
p->reason_bytes[p->byte] = *cur;
cur++;
p->byte++;
}
if (p->byte == 4) {
GPR_ASSERT(is_last);
state->rst_stream = 1;
state->rst_stream_reason =
(((gpr_uint32)p->reason_bytes[0]) << 24) |
(((gpr_uint32)p->reason_bytes[1]) << 16) |
(((gpr_uint32)p->reason_bytes[2]) << 8) |
(((gpr_uint32)p->reason_bytes[3]));
}
return GRPC_CHTTP2_PARSE_OK;
}

@ -35,7 +35,18 @@
#define GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_FRAME_RST_STREAM_H #define GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_FRAME_RST_STREAM_H
#include <grpc/support/slice.h> #include <grpc/support/slice.h>
#include "src/core/transport/chttp2/frame.h"
typedef struct {
gpr_uint8 byte;
gpr_uint8 reason_bytes[4];
} grpc_chttp2_rst_stream_parser;
gpr_slice grpc_chttp2_rst_stream_create(gpr_uint32 stream_id, gpr_uint32 code); gpr_slice grpc_chttp2_rst_stream_create(gpr_uint32 stream_id, gpr_uint32 code);
grpc_chttp2_parse_error grpc_chttp2_rst_stream_parser_begin_frame(
grpc_chttp2_rst_stream_parser *parser, gpr_uint32 length, gpr_uint8 flags);
grpc_chttp2_parse_error grpc_chttp2_rst_stream_parser_parse(
void *parser, grpc_chttp2_parse_state *state, gpr_slice slice, int is_last);
#endif /* GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_FRAME_RST_STREAM_H */ #endif /* GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_FRAME_RST_STREAM_H */

@ -654,7 +654,7 @@ static int parse_stream_weight(grpc_chttp2_hpack_parser *p,
return 1; return 1;
} }
return parse_begin(p, cur + 1, end); return p->after_prioritization(p, cur + 1, end);
} }
static int parse_stream_dep3(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, static int parse_stream_dep3(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur,
@ -1349,7 +1349,7 @@ void grpc_chttp2_hpack_parser_init(grpc_chttp2_hpack_parser *p,
} }
void grpc_chttp2_hpack_parser_set_has_priority(grpc_chttp2_hpack_parser *p) { void grpc_chttp2_hpack_parser_set_has_priority(grpc_chttp2_hpack_parser *p) {
GPR_ASSERT(p->state == parse_begin); p->after_prioritization = p->state;
p->state = parse_stream_dep0; p->state = parse_stream_dep0;
} }

@ -62,6 +62,8 @@ struct grpc_chttp2_hpack_parser {
grpc_chttp2_hpack_parser_state state; grpc_chttp2_hpack_parser_state state;
/* future states dependent on the opening op code */ /* future states dependent on the opening op code */
const grpc_chttp2_hpack_parser_state *next_state; const grpc_chttp2_hpack_parser_state *next_state;
/* what to do after skipping prioritization data */
grpc_chttp2_hpack_parser_state after_prioritization;
/* the value we're currently parsing */ /* the value we're currently parsing */
union { union {
gpr_uint32 *value; gpr_uint32 *value;

@ -154,7 +154,13 @@ typedef enum {
WRITE_STATE_OPEN, WRITE_STATE_OPEN,
WRITE_STATE_QUEUED_CLOSE, WRITE_STATE_QUEUED_CLOSE,
WRITE_STATE_SENT_CLOSE WRITE_STATE_SENT_CLOSE
} WRITE_STATE; } write_state;
typedef enum {
DONT_SEND_CLOSED = 0,
SEND_CLOSED,
SEND_CLOSED_WITH_RST_STREAM
} send_closed;
typedef struct { typedef struct {
stream *head; stream *head;
@ -267,6 +273,7 @@ struct transport {
grpc_chttp2_window_update_parser window_update; grpc_chttp2_window_update_parser window_update;
grpc_chttp2_settings_parser settings; grpc_chttp2_settings_parser settings;
grpc_chttp2_ping_parser ping; grpc_chttp2_ping_parser ping;
grpc_chttp2_rst_stream_parser rst_stream;
} simple_parsers; } simple_parsers;
/* goaway */ /* goaway */
@ -312,8 +319,8 @@ struct stream {
/* when the application requests writes be closed, the write_closed is /* when the application requests writes be closed, the write_closed is
'queued'; when the close is flow controlled into the send path, we are 'queued'; when the close is flow controlled into the send path, we are
'sending' it; when the write has been performed it is 'sent' */ 'sending' it; when the write has been performed it is 'sent' */
WRITE_STATE write_state; write_state write_state;
gpr_uint8 send_closed; send_closed send_closed;
gpr_uint8 read_closed; gpr_uint8 read_closed;
gpr_uint8 cancelled; gpr_uint8 cancelled;
@ -937,7 +944,11 @@ static int prepare_write(transport *t) {
if (s->write_state == WRITE_STATE_QUEUED_CLOSE && if (s->write_state == WRITE_STATE_QUEUED_CLOSE &&
s->outgoing_sopb->nops == 0) { s->outgoing_sopb->nops == 0) {
s->send_closed = 1; if (!t->is_client && !s->read_closed) {
s->send_closed = SEND_CLOSED_WITH_RST_STREAM;
} else {
s->send_closed = SEND_CLOSED;
}
} }
if (s->writing_sopb.nops > 0 || s->send_closed) { if (s->writing_sopb.nops > 0 || s->send_closed) {
stream_list_join(t, s, WRITING); stream_list_join(t, s, WRITING);
@ -982,9 +993,12 @@ static void finalize_outbuf(transport *t) {
while ((s = stream_list_remove_head(t, WRITING))) { while ((s = stream_list_remove_head(t, WRITING))) {
grpc_chttp2_encode(s->writing_sopb.ops, s->writing_sopb.nops, grpc_chttp2_encode(s->writing_sopb.ops, s->writing_sopb.nops,
s->send_closed, s->id, &t->hpack_compressor, &t->outbuf); s->send_closed != DONT_SEND_CLOSED, s->id, &t->hpack_compressor, &t->outbuf);
s->writing_sopb.nops = 0; s->writing_sopb.nops = 0;
if (s->send_closed) { if (s->send_closed == SEND_CLOSED_WITH_RST_STREAM) {
gpr_slice_buffer_add(&t->outbuf, grpc_chttp2_rst_stream_create(s->id, GRPC_CHTTP2_NO_ERROR));
}
if (s->send_closed != DONT_SEND_CLOSED) {
stream_list_join(t, s, WRITTEN_CLOSED); stream_list_join(t, s, WRITTEN_CLOSED);
} }
} }
@ -999,9 +1013,10 @@ static void finish_write_common(transport *t, int success) {
} }
while ((s = stream_list_remove_head(t, WRITTEN_CLOSED))) { while ((s = stream_list_remove_head(t, WRITTEN_CLOSED))) {
s->write_state = WRITE_STATE_SENT_CLOSE; s->write_state = WRITE_STATE_SENT_CLOSE;
if (1||!s->cancelled) { if (!t->is_client) {
maybe_finish_read(t, s); s->read_closed = 1;
} }
maybe_finish_read(t, s);
} }
t->outbuf.count = 0; t->outbuf.count = 0;
t->outbuf.length = 0; t->outbuf.length = 0;
@ -1127,6 +1142,7 @@ static void perform_op_locked(transport *t, stream *s, grpc_transport_op *op) {
if (op->recv_ops) { if (op->recv_ops) {
GPR_ASSERT(s->incoming_sopb == NULL); GPR_ASSERT(s->incoming_sopb == NULL);
GPR_ASSERT(s->published_state != GRPC_STREAM_CLOSED);
s->recv_done_closure.cb = op->on_done_recv; s->recv_done_closure.cb = op->on_done_recv;
s->recv_done_closure.user_data = op->recv_user_data; s->recv_done_closure.user_data = op->recv_user_data;
s->incoming_sopb = op->recv_ops; s->incoming_sopb = op->recv_ops;
@ -1214,12 +1230,14 @@ static void cancel_stream_inner(transport *t, stream *s, gpr_uint32 id,
if (s) { if (s) {
/* clear out any unreported input & output: nobody cares anymore */ /* clear out any unreported input & output: nobody cares anymore */
had_outgoing = s->outgoing_sopb && s->outgoing_sopb->nops != 0; had_outgoing = s->outgoing_sopb && s->outgoing_sopb->nops != 0;
schedule_nuke_sopb(t, &s->parser.incoming_sopb); if (error_code != GRPC_CHTTP2_NO_ERROR) {
if (s->outgoing_sopb) { schedule_nuke_sopb(t, &s->parser.incoming_sopb);
schedule_nuke_sopb(t, s->outgoing_sopb); if (s->outgoing_sopb) {
s->outgoing_sopb = NULL; schedule_nuke_sopb(t, s->outgoing_sopb);
stream_list_remove(t, s, WRITABLE); s->outgoing_sopb = NULL;
schedule_cb(t, s->send_done_closure, 0); stream_list_remove(t, s, WRITABLE);
schedule_cb(t, s->send_done_closure, 0);
}
} }
if (s->cancelled) { if (s->cancelled) {
send_rst = 0; send_rst = 0;
@ -1228,31 +1246,34 @@ static void cancel_stream_inner(transport *t, stream *s, gpr_uint32 id,
s->cancelled = 1; s->cancelled = 1;
stream_list_join(t, s, CANCELLED); stream_list_join(t, s, CANCELLED);
gpr_ltoa(local_status, buffer); if (error_code != GRPC_CHTTP2_NO_ERROR) {
add_incoming_metadata( /* synthesize a status if we don't believe we'll get one */
t, s, gpr_ltoa(local_status, buffer);
grpc_mdelem_from_strings(t->metadata_context, "grpc-status", buffer));
if (!optional_message) {
switch (local_status) {
case GRPC_STATUS_CANCELLED:
add_incoming_metadata(
t, s, grpc_mdelem_from_strings(t->metadata_context,
"grpc-message", "Cancelled"));
break;
default:
break;
}
} else {
add_incoming_metadata( add_incoming_metadata(
t, s, t, s,
grpc_mdelem_from_metadata_strings( grpc_mdelem_from_strings(t->metadata_context, "grpc-status", buffer));
t->metadata_context, if (!optional_message) {
grpc_mdstr_from_string(t->metadata_context, "grpc-message"), switch (local_status) {
grpc_mdstr_ref(optional_message))); case GRPC_STATUS_CANCELLED:
add_incoming_metadata(
t, s, grpc_mdelem_from_strings(t->metadata_context,
"grpc-message", "Cancelled"));
break;
default:
break;
}
} else {
add_incoming_metadata(
t, s,
grpc_mdelem_from_metadata_strings(
t->metadata_context,
grpc_mdstr_from_string(t->metadata_context, "grpc-message"),
grpc_mdstr_ref(optional_message)));
}
add_metadata_batch(t, s);
} }
add_metadata_batch(t, s);
maybe_finish_read(t, s);
} }
maybe_finish_read(t, s);
} }
if (!id) send_rst = 0; if (!id) send_rst = 0;
if (send_rst) { if (send_rst) {
@ -1527,6 +1548,19 @@ static int init_ping_parser(transport *t) {
return ok; return ok;
} }
static int init_rst_stream_parser(transport *t) {
int ok = GRPC_CHTTP2_PARSE_OK ==
grpc_chttp2_rst_stream_parser_begin_frame(&t->simple_parsers.rst_stream,
t->incoming_frame_size,
t->incoming_frame_flags);
if (!ok) {
drop_connection(t);
}
t->parser = grpc_chttp2_rst_stream_parser_parse;
t->parser_data = &t->simple_parsers.rst_stream;
return ok;
}
static int init_goaway_parser(transport *t) { static int init_goaway_parser(transport *t) {
int ok = int ok =
GRPC_CHTTP2_PARSE_OK == GRPC_CHTTP2_PARSE_OK ==
@ -1581,12 +1615,7 @@ static int init_frame_parser(transport *t) {
gpr_log(GPR_ERROR, "Unexpected CONTINUATION frame"); gpr_log(GPR_ERROR, "Unexpected CONTINUATION frame");
return 0; return 0;
case GRPC_CHTTP2_FRAME_RST_STREAM: case GRPC_CHTTP2_FRAME_RST_STREAM:
/* TODO(ctiller): actually parse the reason */ return init_rst_stream_parser(t);
cancel_stream_id(
t, t->incoming_stream_id,
grpc_chttp2_http2_error_to_grpc_status(GRPC_CHTTP2_CANCEL),
GRPC_CHTTP2_CANCEL, 0);
return init_skip_frame(t, 0);
case GRPC_CHTTP2_FRAME_SETTINGS: case GRPC_CHTTP2_FRAME_SETTINGS:
return init_settings_frame_parser(t); return init_settings_frame_parser(t);
case GRPC_CHTTP2_FRAME_WINDOW_UPDATE: case GRPC_CHTTP2_FRAME_WINDOW_UPDATE:
@ -1650,6 +1679,12 @@ static int parse_frame_slice(transport *t, gpr_slice slice, int is_last) {
if (st.goaway) { if (st.goaway) {
add_goaway(t, st.goaway_error, st.goaway_text); add_goaway(t, st.goaway_error, st.goaway_text);
} }
if (st.rst_stream) {
cancel_stream_id(
t, t->incoming_stream_id,
grpc_chttp2_http2_error_to_grpc_status(st.rst_stream_reason),
st.rst_stream_reason, 0);
}
if (st.process_ping_reply) { if (st.process_ping_reply) {
for (i = 0; i < t->ping_count; i++) { for (i = 0; i < t->ping_count; i++) {
if (0 == if (0 ==

@ -34,6 +34,7 @@
#include <grpc++/client_context.h> #include <grpc++/client_context.h>
#include <grpc/grpc.h> #include <grpc/grpc.h>
#include <grpc++/credentials.h>
#include <grpc++/time.h> #include <grpc++/time.h>
namespace grpc { namespace grpc {
@ -63,6 +64,17 @@ void ClientContext::AddMetadata(const grpc::string& meta_key,
send_initial_metadata_.insert(std::make_pair(meta_key, meta_value)); send_initial_metadata_.insert(std::make_pair(meta_key, meta_value));
} }
void ClientContext::set_call(grpc_call* call,
const std::shared_ptr<ChannelInterface>& channel) {
GPR_ASSERT(call_ == nullptr);
call_ = call;
channel_ = channel;
if (creds_ && !creds_->ApplyToCall(call_)) {
grpc_call_cancel_with_status(call, GRPC_STATUS_CANCELLED,
"Failed to set credentials to rpc.");
}
}
void ClientContext::TryCancel() { void ClientContext::TryCancel() {
if (call_) { if (call_) {
grpc_call_cancel(call_); grpc_call_cancel(call_);

@ -41,7 +41,7 @@ namespace grpc {
class ChannelArguments; class ChannelArguments;
std::shared_ptr<ChannelInterface> CreateChannel( std::shared_ptr<ChannelInterface> CreateChannel(
const grpc::string& target, const std::unique_ptr<Credentials>& creds, const grpc::string& target, const std::shared_ptr<Credentials>& creds,
const ChannelArguments& args) { const ChannelArguments& args) {
return creds ? creds->CreateChannel(target, args) return creds ? creds->CreateChannel(target, args)
: std::shared_ptr<ChannelInterface>( : std::shared_ptr<ChannelInterface>(

@ -52,12 +52,15 @@ class InsecureCredentialsImpl GRPC_FINAL : public Credentials {
target, grpc_channel_create(target.c_str(), &channel_args))); target, grpc_channel_create(target.c_str(), &channel_args)));
} }
// InsecureCredentials should not be applied to a call.
bool ApplyToCall(grpc_call* call) GRPC_OVERRIDE { return false; }
SecureCredentials* AsSecureCredentials() GRPC_OVERRIDE { return nullptr; } SecureCredentials* AsSecureCredentials() GRPC_OVERRIDE { return nullptr; }
}; };
} // namespace } // namespace
std::unique_ptr<Credentials> InsecureCredentials() { std::shared_ptr<Credentials> InsecureCredentials() {
return std::unique_ptr<Credentials>(new InsecureCredentialsImpl()); return std::shared_ptr<Credentials>(new InsecureCredentialsImpl());
} }
} // namespace grpc } // namespace grpc

@ -49,20 +49,24 @@ std::shared_ptr<grpc::ChannelInterface> SecureCredentials::CreateChannel(
grpc_secure_channel_create(c_creds_, target.c_str(), &channel_args))); grpc_secure_channel_create(c_creds_, target.c_str(), &channel_args)));
} }
bool SecureCredentials::ApplyToCall(grpc_call* call) {
return grpc_call_set_credentials(call, c_creds_) == GRPC_CALL_OK;
}
namespace { namespace {
std::unique_ptr<Credentials> WrapCredentials(grpc_credentials* creds) { std::shared_ptr<Credentials> WrapCredentials(grpc_credentials* creds) {
return creds == nullptr return creds == nullptr
? nullptr ? nullptr
: std::unique_ptr<Credentials>(new SecureCredentials(creds)); : std::shared_ptr<Credentials>(new SecureCredentials(creds));
} }
} // namespace } // namespace
std::unique_ptr<Credentials> GoogleDefaultCredentials() { std::shared_ptr<Credentials> GoogleDefaultCredentials() {
return WrapCredentials(grpc_google_default_credentials_create()); return WrapCredentials(grpc_google_default_credentials_create());
} }
// Builds SSL Credentials given SSL specific options // Builds SSL Credentials given SSL specific options
std::unique_ptr<Credentials> SslCredentials( std::shared_ptr<Credentials> SslCredentials(
const SslCredentialsOptions& options) { const SslCredentialsOptions& options) {
grpc_ssl_pem_key_cert_pair pem_key_cert_pair = { grpc_ssl_pem_key_cert_pair pem_key_cert_pair = {
options.pem_private_key.c_str(), options.pem_cert_chain.c_str()}; options.pem_private_key.c_str(), options.pem_cert_chain.c_str()};
@ -74,12 +78,12 @@ std::unique_ptr<Credentials> SslCredentials(
} }
// Builds credentials for use when running in GCE // Builds credentials for use when running in GCE
std::unique_ptr<Credentials> ComputeEngineCredentials() { std::shared_ptr<Credentials> ComputeEngineCredentials() {
return WrapCredentials(grpc_compute_engine_credentials_create()); return WrapCredentials(grpc_compute_engine_credentials_create());
} }
// Builds service account credentials. // Builds service account credentials.
std::unique_ptr<Credentials> ServiceAccountCredentials( std::shared_ptr<Credentials> ServiceAccountCredentials(
const grpc::string& json_key, const grpc::string& scope, const grpc::string& json_key, const grpc::string& scope,
long token_lifetime_seconds) { long token_lifetime_seconds) {
if (token_lifetime_seconds <= 0) { if (token_lifetime_seconds <= 0) {
@ -94,8 +98,8 @@ std::unique_ptr<Credentials> ServiceAccountCredentials(
} }
// Builds JWT credentials. // Builds JWT credentials.
std::unique_ptr<Credentials> JWTCredentials( std::shared_ptr<Credentials> JWTCredentials(const grpc::string& json_key,
const grpc::string& json_key, long token_lifetime_seconds) { long token_lifetime_seconds) {
if (token_lifetime_seconds <= 0) { if (token_lifetime_seconds <= 0) {
gpr_log(GPR_ERROR, gpr_log(GPR_ERROR,
"Trying to create JWTCredentials with non-positive lifetime"); "Trying to create JWTCredentials with non-positive lifetime");
@ -107,14 +111,14 @@ std::unique_ptr<Credentials> JWTCredentials(
} }
// Builds refresh token credentials. // Builds refresh token credentials.
std::unique_ptr<Credentials> RefreshTokenCredentials( std::shared_ptr<Credentials> RefreshTokenCredentials(
const grpc::string& json_refresh_token) { const grpc::string& json_refresh_token) {
return WrapCredentials( return WrapCredentials(
grpc_refresh_token_credentials_create(json_refresh_token.c_str())); grpc_refresh_token_credentials_create(json_refresh_token.c_str()));
} }
// Builds IAM credentials. // Builds IAM credentials.
std::unique_ptr<Credentials> IAMCredentials( std::shared_ptr<Credentials> IAMCredentials(
const grpc::string& authorization_token, const grpc::string& authorization_token,
const grpc::string& authority_selector) { const grpc::string& authority_selector) {
return WrapCredentials(grpc_iam_credentials_create( return WrapCredentials(grpc_iam_credentials_create(
@ -122,10 +126,10 @@ std::unique_ptr<Credentials> IAMCredentials(
} }
// Combines two credentials objects into a composite credentials. // Combines two credentials objects into a composite credentials.
std::unique_ptr<Credentials> CompositeCredentials( std::shared_ptr<Credentials> CompositeCredentials(
const std::unique_ptr<Credentials>& creds1, const std::shared_ptr<Credentials>& creds1,
const std::unique_ptr<Credentials>& creds2) { const std::shared_ptr<Credentials>& creds2) {
// Note that we are not saving unique_ptrs to the two credentials // Note that we are not saving shared_ptrs to the two credentials
// passed in here. This is OK because the underlying C objects (i.e., // passed in here. This is OK because the underlying C objects (i.e.,
// creds1 and creds2) into grpc_composite_credentials_create will see their // creds1 and creds2) into grpc_composite_credentials_create will see their
// refcounts incremented. // refcounts incremented.

@ -46,6 +46,7 @@ class SecureCredentials GRPC_FINAL : public Credentials {
explicit SecureCredentials(grpc_credentials* c_creds) : c_creds_(c_creds) {} explicit SecureCredentials(grpc_credentials* c_creds) : c_creds_(c_creds) {}
~SecureCredentials() GRPC_OVERRIDE { grpc_credentials_release(c_creds_); } ~SecureCredentials() GRPC_OVERRIDE { grpc_credentials_release(c_creds_); }
grpc_credentials* GetRawCreds() { return c_creds_; } grpc_credentials* GetRawCreds() { return c_creds_; }
bool ApplyToCall(grpc_call* call) GRPC_OVERRIDE;
std::shared_ptr<grpc::ChannelInterface> CreateChannel( std::shared_ptr<grpc::ChannelInterface> CreateChannel(
const string& target, const grpc::ChannelArguments& args) GRPC_OVERRIDE; const string& target, const grpc::ChannelArguments& args) GRPC_OVERRIDE;

@ -149,7 +149,7 @@ class Server::SyncRequest GRPC_FINAL : public CompletionQueueTag {
} }
buf.AddServerSendStatus(&ctx_.trailing_metadata_, status); buf.AddServerSendStatus(&ctx_.trailing_metadata_, status);
call_.PerformOps(&buf); call_.PerformOps(&buf);
GPR_ASSERT(cq_.Pluck(&buf)); cq_.Pluck(&buf); /* status ignored */
void* ignored_tag; void* ignored_tag;
bool ignored_ok; bool ignored_ok;
cq_.Shutdown(); cq_.Shutdown();

@ -10,6 +10,7 @@
<RootNamespace>Grpc.Auth</RootNamespace> <RootNamespace>Grpc.Auth</RootNamespace>
<AssemblyName>Grpc.Auth</AssemblyName> <AssemblyName>Grpc.Auth</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<DocumentationFile>bin\$(Configuration)\Grpc.Auth.Xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>

@ -22,5 +22,8 @@
</metadata> </metadata>
<files> <files>
<file src="bin/Release/Grpc.Auth.dll" target="lib/net45" /> <file src="bin/Release/Grpc.Auth.dll" target="lib/net45" />
<file src="bin/Release/Grpc.Auth.pdb" target="lib/net45" />
<file src="bin/Release/Grpc.Auth.xml" target="lib/net45" />
<file src="**\*.cs" target="src" />
</files> </files>
</package> </package>

@ -34,6 +34,9 @@
<HintPath>..\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath> <HintPath>..\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Interactive.Async">
<HintPath>..\packages\Ix-Async.1.2.3\lib\net45\System.Interactive.Async.dll</HintPath>
</Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
@ -57,7 +60,5 @@
<ItemGroup> <ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" /> <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup />
<Folder Include="Internal\" />
</ItemGroup>
</Project> </Project>

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="Ix-Async" version="1.2.3" targetFramework="net45" />
<package id="NUnit" version="2.6.4" targetFramework="net45" /> <package id="NUnit" version="2.6.4" targetFramework="net45" />
</packages> </packages>

@ -40,33 +40,17 @@ namespace Grpc.Core
/// <summary> /// <summary>
/// Return type for client streaming calls. /// Return type for client streaming calls.
/// </summary> /// </summary>
public sealed class AsyncClientStreamingCall<TRequest, TResponse> public sealed class AsyncClientStreamingCall<TRequest, TResponse> : IDisposable
where TRequest : class
where TResponse : class
{ {
readonly IClientStreamWriter<TRequest> requestStream; readonly IClientStreamWriter<TRequest> requestStream;
readonly Task<TResponse> result; readonly Task<TResponse> result;
readonly Action disposeAction;
public AsyncClientStreamingCall(IClientStreamWriter<TRequest> requestStream, Task<TResponse> result) public AsyncClientStreamingCall(IClientStreamWriter<TRequest> requestStream, Task<TResponse> result, Action disposeAction)
{ {
this.requestStream = requestStream; this.requestStream = requestStream;
this.result = result; this.result = result;
} this.disposeAction = disposeAction;
/// <summary>
/// Writes a request to RequestStream.
/// </summary>
public Task Write(TRequest message)
{
return requestStream.Write(message);
}
/// <summary>
/// Closes the RequestStream.
/// </summary>
public Task Close()
{
return requestStream.Close();
} }
/// <summary> /// <summary>
@ -99,5 +83,16 @@ namespace Grpc.Core
{ {
return result.GetAwaiter(); return result.GetAwaiter();
} }
/// <summary>
/// Provides means to provide after the call.
/// If the call has already finished normally (request stream has been completed and call result has been received), doesn't do anything.
/// Otherwise, requests cancellation of the call which should terminate all pending async operations associated with the call.
/// As a result, all resources being used by the call should be released eventually.
/// </summary>
public void Dispose()
{
disposeAction.Invoke();
}
} }
} }

@ -40,42 +40,17 @@ namespace Grpc.Core
/// <summary> /// <summary>
/// Return type for bidirectional streaming calls. /// Return type for bidirectional streaming calls.
/// </summary> /// </summary>
public sealed class AsyncDuplexStreamingCall<TRequest, TResponse> public sealed class AsyncDuplexStreamingCall<TRequest, TResponse> : IDisposable
where TRequest : class
where TResponse : class
{ {
readonly IClientStreamWriter<TRequest> requestStream; readonly IClientStreamWriter<TRequest> requestStream;
readonly IAsyncStreamReader<TResponse> responseStream; readonly IAsyncStreamReader<TResponse> responseStream;
readonly Action disposeAction;
public AsyncDuplexStreamingCall(IClientStreamWriter<TRequest> requestStream, IAsyncStreamReader<TResponse> responseStream) public AsyncDuplexStreamingCall(IClientStreamWriter<TRequest> requestStream, IAsyncStreamReader<TResponse> responseStream, Action disposeAction)
{ {
this.requestStream = requestStream; this.requestStream = requestStream;
this.responseStream = responseStream; this.responseStream = responseStream;
} this.disposeAction = disposeAction;
/// <summary>
/// Writes a request to RequestStream.
/// </summary>
public Task Write(TRequest message)
{
return requestStream.Write(message);
}
/// <summary>
/// Closes the RequestStream.
/// </summary>
public Task Close()
{
return requestStream.Close();
}
/// <summary>
/// Reads a response from ResponseStream.
/// </summary>
/// <returns></returns>
public Task<TResponse> ReadNext()
{
return responseStream.ReadNext();
} }
/// <summary> /// <summary>
@ -99,5 +74,16 @@ namespace Grpc.Core
return requestStream; return requestStream;
} }
} }
/// <summary>
/// Provides means to cleanup after the call.
/// If the call has already finished normally (request stream has been completed and response stream has been fully read), doesn't do anything.
/// Otherwise, requests cancellation of the call which should terminate all pending async operations associated with the call.
/// As a result, all resources being used by the call should be released eventually.
/// </summary>
public void Dispose()
{
disposeAction.Invoke();
}
} }
} }

@ -40,23 +40,15 @@ namespace Grpc.Core
/// <summary> /// <summary>
/// Return type for server streaming calls. /// Return type for server streaming calls.
/// </summary> /// </summary>
public sealed class AsyncServerStreamingCall<TResponse> public sealed class AsyncServerStreamingCall<TResponse> : IDisposable
where TResponse : class
{ {
readonly IAsyncStreamReader<TResponse> responseStream; readonly IAsyncStreamReader<TResponse> responseStream;
readonly Action disposeAction;
public AsyncServerStreamingCall(IAsyncStreamReader<TResponse> responseStream) public AsyncServerStreamingCall(IAsyncStreamReader<TResponse> responseStream, Action disposeAction)
{ {
this.responseStream = responseStream; this.responseStream = responseStream;
} this.disposeAction = disposeAction;
/// <summary>
/// Reads the next response from ResponseStream
/// </summary>
/// <returns></returns>
public Task<TResponse> ReadNext()
{
return responseStream.ReadNext();
} }
/// <summary> /// <summary>
@ -69,5 +61,16 @@ namespace Grpc.Core
return responseStream; return responseStream;
} }
} }
/// <summary>
/// Provides means to cleanup after the call.
/// If the call has already finished normally (response stream has been fully read), doesn't do anything.
/// Otherwise, requests cancellation of the call which should terminate all pending async operations associated with the call.
/// As a result, all resources being used by the call should be released eventually.
/// </summary>
public void Dispose()
{
disposeAction.Invoke();
}
} }
} }

@ -41,8 +41,6 @@ namespace Grpc.Core
/// Abstraction of a call to be invoked on a client. /// Abstraction of a call to be invoked on a client.
/// </summary> /// </summary>
public class Call<TRequest, TResponse> public class Call<TRequest, TResponse>
where TRequest : class
where TResponse : class
{ {
readonly string name; readonly string name;
readonly Marshaller<TRequest> requestMarshaller; readonly Marshaller<TRequest> requestMarshaller;

@ -73,7 +73,7 @@ namespace Grpc.Core
asyncCall.StartServerStreamingCall(req, call.Headers); asyncCall.StartServerStreamingCall(req, call.Headers);
RegisterCancellationCallback(asyncCall, token); RegisterCancellationCallback(asyncCall, token);
var responseStream = new ClientResponseStream<TRequest, TResponse>(asyncCall); var responseStream = new ClientResponseStream<TRequest, TResponse>(asyncCall);
return new AsyncServerStreamingCall<TResponse>(responseStream); return new AsyncServerStreamingCall<TResponse>(responseStream, asyncCall.Cancel);
} }
public static AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(Call<TRequest, TResponse> call, CancellationToken token) public static AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(Call<TRequest, TResponse> call, CancellationToken token)
@ -85,7 +85,7 @@ namespace Grpc.Core
var resultTask = asyncCall.ClientStreamingCallAsync(call.Headers); var resultTask = asyncCall.ClientStreamingCallAsync(call.Headers);
RegisterCancellationCallback(asyncCall, token); RegisterCancellationCallback(asyncCall, token);
var requestStream = new ClientRequestStream<TRequest, TResponse>(asyncCall); var requestStream = new ClientRequestStream<TRequest, TResponse>(asyncCall);
return new AsyncClientStreamingCall<TRequest, TResponse>(requestStream, resultTask); return new AsyncClientStreamingCall<TRequest, TResponse>(requestStream, resultTask, asyncCall.Cancel);
} }
public static AsyncDuplexStreamingCall<TRequest, TResponse> AsyncDuplexStreamingCall<TRequest, TResponse>(Call<TRequest, TResponse> call, CancellationToken token) public static AsyncDuplexStreamingCall<TRequest, TResponse> AsyncDuplexStreamingCall<TRequest, TResponse>(Call<TRequest, TResponse> call, CancellationToken token)
@ -98,7 +98,7 @@ namespace Grpc.Core
RegisterCancellationCallback(asyncCall, token); RegisterCancellationCallback(asyncCall, token);
var requestStream = new ClientRequestStream<TRequest, TResponse>(asyncCall); var requestStream = new ClientRequestStream<TRequest, TResponse>(asyncCall);
var responseStream = new ClientResponseStream<TRequest, TResponse>(asyncCall); var responseStream = new ClientResponseStream<TRequest, TResponse>(asyncCall);
return new AsyncDuplexStreamingCall<TRequest, TResponse>(requestStream, responseStream); return new AsyncDuplexStreamingCall<TRequest, TResponse>(requestStream, responseStream, asyncCall.Cancel);
} }
private static void RegisterCancellationCallback<TRequest, TResponse>(AsyncCall<TRequest, TResponse> asyncCall, CancellationToken token) private static void RegisterCancellationCallback<TRequest, TResponse>(AsyncCall<TRequest, TResponse> asyncCall, CancellationToken token)

@ -13,6 +13,7 @@
<AssemblyName>Grpc.Core</AssemblyName> <AssemblyName>Grpc.Core</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<NuGetPackageImportStamp>8bb563fb</NuGetPackageImportStamp> <NuGetPackageImportStamp>8bb563fb</NuGetPackageImportStamp>
<DocumentationFile>bin\$(Configuration)\Grpc.Core.Xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
@ -37,6 +38,9 @@
<Reference Include="System.Collections.Immutable"> <Reference Include="System.Collections.Immutable">
<HintPath>..\packages\Microsoft.Bcl.Immutable.1.0.34\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll</HintPath> <HintPath>..\packages\Microsoft.Bcl.Immutable.1.0.34\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Interactive.Async">
<HintPath>..\packages\Ix-Async.1.2.3\lib\net45\System.Interactive.Async.dll</HintPath>
</Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="AsyncDuplexStreamingCall.cs" /> <Compile Include="AsyncDuplexStreamingCall.cs" />

@ -16,10 +16,14 @@
<tags>gRPC RPC Protocol HTTP/2</tags> <tags>gRPC RPC Protocol HTTP/2</tags>
<dependencies> <dependencies>
<dependency id="Microsoft.Bcl.Immutable" version="1.0.34" /> <dependency id="Microsoft.Bcl.Immutable" version="1.0.34" />
<dependency id="grpc.native.csharp_ext" version="0.8.0.0" /> <dependency id="Ix-Async" version="1.2.3" />
<dependency id="grpc.native.csharp_ext" version="0.9.0.0" />
</dependencies> </dependencies>
</metadata> </metadata>
<files> <files>
<file src="bin/Release/Grpc.Core.dll" target="lib/net45" /> <file src="bin/Release/Grpc.Core.dll" target="lib/net45" />
<file src="bin/Release/Grpc.Core.pdb" target="lib/net45" />
<file src="bin/Release/Grpc.Core.xml" target="lib/net45" />
<file src="**\*.cs" target="src" />
</files> </files>
</package> </package>

@ -43,13 +43,8 @@ namespace Grpc.Core
/// A stream of messages to be read. /// A stream of messages to be read.
/// </summary> /// </summary>
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
public interface IAsyncStreamReader<T> public interface IAsyncStreamReader<TResponse> : IAsyncEnumerator<TResponse>
where T : class
{ {
/// <summary> // TODO(jtattermusch): consider just using IAsyncEnumerator instead of this interface.
/// Reads a single message. Returns null if the last message was already read.
/// A following read can only be started when the previous one finishes.
/// </summary>
Task<T> ReadNext();
} }
} }

@ -44,12 +44,11 @@ namespace Grpc.Core
/// </summary> /// </summary>
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
public interface IAsyncStreamWriter<T> public interface IAsyncStreamWriter<T>
where T : class
{ {
/// <summary> /// <summary>
/// Writes a single message. Only one write can be pending at a time. /// Writes a single asynchronously. Only one write can be pending at a time.
/// </summary> /// </summary>
/// <param name="message">the message to be written. Cannot be null.</param> /// <param name="message">the message to be written. Cannot be null.</param>
Task Write(T message); Task WriteAsync(T message);
} }
} }

@ -44,11 +44,10 @@ namespace Grpc.Core
/// </summary> /// </summary>
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
public interface IClientStreamWriter<T> : IAsyncStreamWriter<T> public interface IClientStreamWriter<T> : IAsyncStreamWriter<T>
where T : class
{ {
/// <summary> /// <summary>
/// Closes the stream. Can only be called once there is no pending write. No writes should follow calling this. /// Completes/closes the stream. Can only be called once there is no pending write. No writes should follow calling this.
/// </summary> /// </summary>
Task Close(); Task CompleteAsync();
} }
} }

@ -43,7 +43,7 @@ namespace Grpc.Core
/// A writable stream of messages that is used in server-side handlers. /// A writable stream of messages that is used in server-side handlers.
/// </summary> /// </summary>
public interface IServerStreamWriter<T> : IAsyncStreamWriter<T> public interface IServerStreamWriter<T> : IAsyncStreamWriter<T>
where T : class
{ {
// TODO(jtattermusch): consider just using IAsyncStreamWriter instead of this interface.
} }
} }

@ -38,8 +38,6 @@ namespace Grpc.Core.Internal
/// Writes requests asynchronously to an underlying AsyncCall object. /// Writes requests asynchronously to an underlying AsyncCall object.
/// </summary> /// </summary>
internal class ClientRequestStream<TRequest, TResponse> : IClientStreamWriter<TRequest> internal class ClientRequestStream<TRequest, TResponse> : IClientStreamWriter<TRequest>
where TRequest : class
where TResponse : class
{ {
readonly AsyncCall<TRequest, TResponse> call; readonly AsyncCall<TRequest, TResponse> call;
@ -48,14 +46,14 @@ namespace Grpc.Core.Internal
this.call = call; this.call = call;
} }
public Task Write(TRequest message) public Task WriteAsync(TRequest message)
{ {
var taskSource = new AsyncCompletionTaskSource<object>(); var taskSource = new AsyncCompletionTaskSource<object>();
call.StartSendMessage(message, taskSource.CompletionDelegate); call.StartSendMessage(message, taskSource.CompletionDelegate);
return taskSource.Task; return taskSource.Task;
} }
public Task Close() public Task CompleteAsync()
{ {
var taskSource = new AsyncCompletionTaskSource<object>(); var taskSource = new AsyncCompletionTaskSource<object>();
call.StartSendCloseFromClient(taskSource.CompletionDelegate); call.StartSendCloseFromClient(taskSource.CompletionDelegate);

@ -33,6 +33,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Grpc.Core.Internal namespace Grpc.Core.Internal
@ -42,17 +43,41 @@ namespace Grpc.Core.Internal
where TResponse : class where TResponse : class
{ {
readonly AsyncCall<TRequest, TResponse> call; readonly AsyncCall<TRequest, TResponse> call;
TResponse current;
public ClientResponseStream(AsyncCall<TRequest, TResponse> call) public ClientResponseStream(AsyncCall<TRequest, TResponse> call)
{ {
this.call = call; this.call = call;
} }
public Task<TResponse> ReadNext() public TResponse Current
{ {
get
{
if (current == null)
{
throw new InvalidOperationException("No current element is available.");
}
return current;
}
}
public async Task<bool> MoveNext(CancellationToken token)
{
if (token != CancellationToken.None)
{
throw new InvalidOperationException("Cancellation of individual reads is not supported.");
}
var taskSource = new AsyncCompletionTaskSource<TResponse>(); var taskSource = new AsyncCompletionTaskSource<TResponse>();
call.StartReadMessage(taskSource.CompletionDelegate); call.StartReadMessage(taskSource.CompletionDelegate);
return taskSource.Task; var result = await taskSource.Task;
this.current = result;
return result != null;
}
public void Dispose()
{
// TODO(jtattermusch): implement the semantics of stream disposal.
} }
} }
} }

@ -32,6 +32,7 @@
#endregion #endregion
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Grpc.Core.Internal; using Grpc.Core.Internal;
@ -71,12 +72,13 @@ namespace Grpc.Core.Internal
Status status = Status.DefaultSuccess; Status status = Status.DefaultSuccess;
try try
{ {
var request = await requestStream.ReadNext(); Preconditions.CheckArgument(await requestStream.MoveNext());
var request = requestStream.Current;
// TODO(jtattermusch): we need to read the full stream so that native callhandle gets deallocated. // TODO(jtattermusch): we need to read the full stream so that native callhandle gets deallocated.
Preconditions.CheckArgument(await requestStream.ReadNext() == null); Preconditions.CheckArgument(!await requestStream.MoveNext());
var context = new ServerCallContext(); // TODO(jtattermusch): initialize the context var context = new ServerCallContext(); // TODO(jtattermusch): initialize the context
var result = await handler(context, request); var result = await handler(context, request);
await responseStream.Write(result); await responseStream.WriteAsync(result);
} }
catch (Exception e) catch (Exception e)
{ {
@ -85,7 +87,7 @@ namespace Grpc.Core.Internal
} }
try try
{ {
await responseStream.WriteStatus(status); await responseStream.WriteStatusAsync(status);
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
@ -122,9 +124,10 @@ namespace Grpc.Core.Internal
Status status = Status.DefaultSuccess; Status status = Status.DefaultSuccess;
try try
{ {
var request = await requestStream.ReadNext(); Preconditions.CheckArgument(await requestStream.MoveNext());
var request = requestStream.Current;
// TODO(jtattermusch): we need to read the full stream so that native callhandle gets deallocated. // TODO(jtattermusch): we need to read the full stream so that native callhandle gets deallocated.
Preconditions.CheckArgument(await requestStream.ReadNext() == null); Preconditions.CheckArgument(!await requestStream.MoveNext());
var context = new ServerCallContext(); // TODO(jtattermusch): initialize the context var context = new ServerCallContext(); // TODO(jtattermusch): initialize the context
await handler(context, request, responseStream); await handler(context, request, responseStream);
@ -137,7 +140,7 @@ namespace Grpc.Core.Internal
try try
{ {
await responseStream.WriteStatus(status); await responseStream.WriteStatusAsync(status);
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
@ -178,7 +181,7 @@ namespace Grpc.Core.Internal
var result = await handler(context, requestStream); var result = await handler(context, requestStream);
try try
{ {
await responseStream.Write(result); await responseStream.WriteAsync(result);
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
@ -193,7 +196,7 @@ namespace Grpc.Core.Internal
try try
{ {
await responseStream.WriteStatus(status); await responseStream.WriteStatusAsync(status);
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
@ -240,7 +243,7 @@ namespace Grpc.Core.Internal
} }
try try
{ {
await responseStream.WriteStatus(status); await responseStream.WriteStatusAsync(status);
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
@ -263,7 +266,7 @@ namespace Grpc.Core.Internal
var requestStream = new ServerRequestStream<byte[], byte[]>(asyncCall); var requestStream = new ServerRequestStream<byte[], byte[]>(asyncCall);
var responseStream = new ServerResponseStream<byte[], byte[]>(asyncCall); var responseStream = new ServerResponseStream<byte[], byte[]>(asyncCall);
await responseStream.WriteStatus(new Status(StatusCode.Unimplemented, "No such method.")); await responseStream.WriteStatusAsync(new Status(StatusCode.Unimplemented, "No such method."));
// TODO(jtattermusch): if we don't read what client has sent, the server call never gets disposed. // TODO(jtattermusch): if we don't read what client has sent, the server call never gets disposed.
await requestStream.ToList(); await requestStream.ToList();
await finishedTask; await finishedTask;

@ -33,6 +33,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Grpc.Core.Internal namespace Grpc.Core.Internal
@ -42,17 +43,41 @@ namespace Grpc.Core.Internal
where TResponse : class where TResponse : class
{ {
readonly AsyncCallServer<TRequest, TResponse> call; readonly AsyncCallServer<TRequest, TResponse> call;
TRequest current;
public ServerRequestStream(AsyncCallServer<TRequest, TResponse> call) public ServerRequestStream(AsyncCallServer<TRequest, TResponse> call)
{ {
this.call = call; this.call = call;
} }
public Task<TRequest> ReadNext() public TRequest Current
{ {
get
{
if (current == null)
{
throw new InvalidOperationException("No current element is available.");
}
return current;
}
}
public async Task<bool> MoveNext(CancellationToken token)
{
if (token != CancellationToken.None)
{
throw new InvalidOperationException("Cancellation of individual reads is not supported.");
}
var taskSource = new AsyncCompletionTaskSource<TRequest>(); var taskSource = new AsyncCompletionTaskSource<TRequest>();
call.StartReadMessage(taskSource.CompletionDelegate); call.StartReadMessage(taskSource.CompletionDelegate);
return taskSource.Task; var result = await taskSource.Task;
this.current = result;
return result != null;
}
public void Dispose()
{
// TODO(jtattermusch): implement the semantics of stream disposal.
} }
} }
} }

@ -49,14 +49,14 @@ namespace Grpc.Core.Internal
this.call = call; this.call = call;
} }
public Task Write(TResponse message) public Task WriteAsync(TResponse message)
{ {
var taskSource = new AsyncCompletionTaskSource<object>(); var taskSource = new AsyncCompletionTaskSource<object>();
call.StartSendMessage(message, taskSource.CompletionDelegate); call.StartSendMessage(message, taskSource.CompletionDelegate);
return taskSource.Task; return taskSource.Task;
} }
public Task WriteStatus(Status status) public Task WriteStatusAsync(Status status)
{ {
var taskSource = new AsyncCompletionTaskSource<object>(); var taskSource = new AsyncCompletionTaskSource<object>();
call.StartSendStatusFromServer(status, taskSource.CompletionDelegate); call.StartSendStatusFromServer(status, taskSource.CompletionDelegate);

@ -39,9 +39,6 @@ using Grpc.Core.Utils;
namespace Grpc.Core.Internal namespace Grpc.Core.Internal
{ {
// TODO: we need to make sure that the delegates are not collected before invoked.
//internal delegate void ServerShutdownCallbackDelegate(bool success);
/// <summary> /// <summary>
/// grpc_server from grpc/grpc.h /// grpc_server from grpc/grpc.h
/// </summary> /// </summary>

@ -42,7 +42,6 @@ namespace Grpc.Core
/// </summary> /// </summary>
public sealed class ServerCallContext public sealed class ServerCallContext
{ {
// TODO(jtattermusch): add cancellationToken // TODO(jtattermusch): add cancellationToken
// TODO(jtattermusch): add deadline info // TODO(jtattermusch): add deadline info

@ -49,14 +49,9 @@ namespace Grpc.Core.Utils
public static async Task ForEach<T>(this IAsyncStreamReader<T> streamReader, Func<T, Task> asyncAction) public static async Task ForEach<T>(this IAsyncStreamReader<T> streamReader, Func<T, Task> asyncAction)
where T : class where T : class
{ {
while (true) while (await streamReader.MoveNext())
{ {
var elem = await streamReader.ReadNext(); await asyncAction(streamReader.Current);
if (elem == null)
{
break;
}
await asyncAction(elem);
} }
} }
@ -67,32 +62,27 @@ namespace Grpc.Core.Utils
where T : class where T : class
{ {
var result = new List<T>(); var result = new List<T>();
while (true) while (await streamReader.MoveNext())
{ {
var elem = await streamReader.ReadNext(); result.Add(streamReader.Current);
if (elem == null)
{
break;
}
result.Add(elem);
} }
return result; return result;
} }
/// <summary> /// <summary>
/// Writes all elements from given enumerable to the stream. /// Writes all elements from given enumerable to the stream.
/// Closes the stream afterwards unless close = false. /// Completes the stream afterwards unless close = false.
/// </summary> /// </summary>
public static async Task WriteAll<T>(this IClientStreamWriter<T> streamWriter, IEnumerable<T> elements, bool close = true) public static async Task WriteAll<T>(this IClientStreamWriter<T> streamWriter, IEnumerable<T> elements, bool complete = true)
where T : class where T : class
{ {
foreach (var element in elements) foreach (var element in elements)
{ {
await streamWriter.Write(element); await streamWriter.WriteAsync(element);
} }
if (close) if (complete)
{ {
await streamWriter.Close(); await streamWriter.CompleteAsync();
} }
} }
@ -104,7 +94,7 @@ namespace Grpc.Core.Utils
{ {
foreach (var element in elements) foreach (var element in elements)
{ {
await streamWriter.Write(element); await streamWriter.WriteAsync(element);
} }
} }
} }

@ -2,5 +2,6 @@
<packages> <packages>
<package id="grpc.dependencies.openssl.redist" version="1.0.2.2" targetFramework="net45" /> <package id="grpc.dependencies.openssl.redist" version="1.0.2.2" targetFramework="net45" />
<package id="grpc.dependencies.zlib.redist" version="1.2.8.9" targetFramework="net45" /> <package id="grpc.dependencies.zlib.redist" version="1.2.8.9" targetFramework="net45" />
<package id="Ix-Async" version="1.2.3" targetFramework="net45" />
<package id="Microsoft.Bcl.Immutable" version="1.0.34" targetFramework="net45" /> <package id="Microsoft.Bcl.Immutable" version="1.0.34" targetFramework="net45" />
</packages> </packages>

@ -37,6 +37,10 @@
<Reference Include="Google.ProtocolBuffers"> <Reference Include="Google.ProtocolBuffers">
<HintPath>..\packages\Google.ProtocolBuffers.2.4.1.521\lib\net40\Google.ProtocolBuffers.dll</HintPath> <HintPath>..\packages\Google.ProtocolBuffers.2.4.1.521\lib\net40\Google.ProtocolBuffers.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Interactive.Async, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Ix-Async.1.2.3\lib\net45\System.Interactive.Async.dll</HintPath>
</Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />

@ -96,7 +96,19 @@ namespace math.Tests
Assert.AreEqual(0, response.Remainder); Assert.AreEqual(0, response.Remainder);
} }
// TODO(jtattermusch): test division by zero [Test]
public void DivByZero()
{
try
{
DivReply response = client.Div(new DivArgs.Builder { Dividend = 0, Divisor = 0 }.Build());
Assert.Fail();
}
catch (RpcException e)
{
Assert.AreEqual(StatusCode.Unknown, e.Status.StatusCode);
}
}
[Test] [Test]
public void DivAsync() public void DivAsync()
@ -114,11 +126,12 @@ namespace math.Tests
{ {
Task.Run(async () => Task.Run(async () =>
{ {
var call = client.Fib(new FibArgs.Builder { Limit = 6 }.Build()); using (var call = client.Fib(new FibArgs.Builder { Limit = 6 }.Build()))
{
var responses = await call.ResponseStream.ToList(); var responses = await call.ResponseStream.ToList();
CollectionAssert.AreEqual(new List<long> { 1, 1, 2, 3, 5, 8 }, CollectionAssert.AreEqual(new List<long> { 1, 1, 2, 3, 5, 8 },
responses.ConvertAll((n) => n.Num_)); responses.ConvertAll((n) => n.Num_));
}
}).Wait(); }).Wait();
} }
@ -128,13 +141,15 @@ namespace math.Tests
{ {
Task.Run(async () => Task.Run(async () =>
{ {
var call = client.Sum(); using (var call = client.Sum())
var numbers = new List<long> { 10, 20, 30 }.ConvertAll( {
n => Num.CreateBuilder().SetNum_(n).Build()); var numbers = new List<long> { 10, 20, 30 }.ConvertAll(
n => Num.CreateBuilder().SetNum_(n).Build());
await call.RequestStream.WriteAll(numbers); await call.RequestStream.WriteAll(numbers);
var result = await call.Result; var result = await call.Result;
Assert.AreEqual(60, result.Num_); Assert.AreEqual(60, result.Num_);
}
}).Wait(); }).Wait();
} }
@ -150,12 +165,14 @@ namespace math.Tests
new DivArgs.Builder { Dividend = 7, Divisor = 2 }.Build() new DivArgs.Builder { Dividend = 7, Divisor = 2 }.Build()
}; };
var call = client.DivMany(); using (var call = client.DivMany())
await call.RequestStream.WriteAll(divArgsList); {
var result = await call.ResponseStream.ToList(); await call.RequestStream.WriteAll(divArgsList);
var result = await call.ResponseStream.ToList();
CollectionAssert.AreEqual(new long[] { 3, 4, 3 }, result.ConvertAll((divReply) => divReply.Quotient)); CollectionAssert.AreEqual(new long[] { 3, 4, 3 }, result.ConvertAll((divReply) => divReply.Quotient));
CollectionAssert.AreEqual(new long[] { 1, 16, 1 }, result.ConvertAll((divReply) => divReply.Remainder)); CollectionAssert.AreEqual(new long[] { 1, 16, 1 }, result.ConvertAll((divReply) => divReply.Remainder));
}
}).Wait(); }).Wait();
} }
} }

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="Google.ProtocolBuffers" version="2.4.1.521" targetFramework="net45" /> <package id="Google.ProtocolBuffers" version="2.4.1.521" targetFramework="net45" />
<package id="Ix-Async" version="1.2.3" targetFramework="net45" />
<package id="NUnit" version="2.6.4" targetFramework="net45" /> <package id="NUnit" version="2.6.4" targetFramework="net45" />
</packages> </packages>

@ -35,6 +35,9 @@
<Reference Include="Google.ProtocolBuffers"> <Reference Include="Google.ProtocolBuffers">
<HintPath>..\packages\Google.ProtocolBuffers.2.4.1.521\lib\net40\Google.ProtocolBuffers.dll</HintPath> <HintPath>..\packages\Google.ProtocolBuffers.2.4.1.521\lib\net40\Google.ProtocolBuffers.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Interactive.Async">
<HintPath>..\packages\Ix-Async.1.2.3\lib\net45\System.Interactive.Async.dll</HintPath>
</Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />

@ -51,18 +51,13 @@ namespace math
Console.WriteLine("DivAsync Result: " + result); Console.WriteLine("DivAsync Result: " + result);
} }
public static async Task DivAsyncWithCancellationExample(Math.IMathClient stub)
{
Task<DivReply> resultTask = stub.DivAsync(new DivArgs.Builder { Dividend = 4, Divisor = 5 }.Build());
DivReply result = await resultTask;
Console.WriteLine(result);
}
public static async Task FibExample(Math.IMathClient stub) public static async Task FibExample(Math.IMathClient stub)
{ {
var call = stub.Fib(new FibArgs.Builder { Limit = 5 }.Build()); using (var call = stub.Fib(new FibArgs.Builder { Limit = 5 }.Build()))
List<Num> result = await call.ResponseStream.ToList(); {
Console.WriteLine("Fib Result: " + string.Join("|", result)); List<Num> result = await call.ResponseStream.ToList();
Console.WriteLine("Fib Result: " + string.Join("|", result));
}
} }
public static async Task SumExample(Math.IMathClient stub) public static async Task SumExample(Math.IMathClient stub)
@ -74,9 +69,11 @@ namespace math
new Num.Builder { Num_ = 3 }.Build() new Num.Builder { Num_ = 3 }.Build()
}; };
var call = stub.Sum(); using (var call = stub.Sum())
await call.RequestStream.WriteAll(numbers); {
Console.WriteLine("Sum Result: " + await call.Result); await call.RequestStream.WriteAll(numbers);
Console.WriteLine("Sum Result: " + await call.Result);
}
} }
public static async Task DivManyExample(Math.IMathClient stub) public static async Task DivManyExample(Math.IMathClient stub)
@ -87,9 +84,11 @@ namespace math
new DivArgs.Builder { Dividend = 100, Divisor = 21 }.Build(), new DivArgs.Builder { Dividend = 100, Divisor = 21 }.Build(),
new DivArgs.Builder { Dividend = 7, Divisor = 2 }.Build() new DivArgs.Builder { Dividend = 7, Divisor = 2 }.Build()
}; };
var call = stub.DivMany(); using (var call = stub.DivMany())
await call.RequestStream.WriteAll(divArgsList); {
Console.WriteLine("DivMany Result: " + string.Join("|", await call.ResponseStream.ToList())); await call.RequestStream.WriteAll(divArgsList);
Console.WriteLine("DivMany Result: " + string.Join("|", await call.ResponseStream.ToList()));
}
} }
public static async Task DependendRequestsExample(Math.IMathClient stub) public static async Task DependendRequestsExample(Math.IMathClient stub)
@ -101,9 +100,12 @@ namespace math
new Num.Builder { Num_ = 3 }.Build() new Num.Builder { Num_ = 3 }.Build()
}; };
var sumCall = stub.Sum(); Num sum;
await sumCall.RequestStream.WriteAll(numbers); using (var sumCall = stub.Sum())
Num sum = await sumCall.Result; {
await sumCall.RequestStream.WriteAll(numbers);
sum = await sumCall.Result;
}
DivReply result = await stub.DivAsync(new DivArgs.Builder { Dividend = sum.Num_, Divisor = numbers.Count }.Build()); DivReply result = await stub.DivAsync(new DivArgs.Builder { Dividend = sum.Num_, Divisor = numbers.Count }.Build());
Console.WriteLine("Avg Result: " + result); Console.WriteLine("Avg Result: " + result);

@ -12,30 +12,30 @@ namespace math {
{ {
static readonly string __ServiceName = "math.Math"; static readonly string __ServiceName = "math.Math";
static readonly Marshaller<DivArgs> __Marshaller_DivArgs = Marshallers.Create((arg) => arg.ToByteArray(), DivArgs.ParseFrom); static readonly Marshaller<global::math.DivArgs> __Marshaller_DivArgs = Marshallers.Create((arg) => arg.ToByteArray(), global::math.DivArgs.ParseFrom);
static readonly Marshaller<DivReply> __Marshaller_DivReply = Marshallers.Create((arg) => arg.ToByteArray(), DivReply.ParseFrom); static readonly Marshaller<global::math.DivReply> __Marshaller_DivReply = Marshallers.Create((arg) => arg.ToByteArray(), global::math.DivReply.ParseFrom);
static readonly Marshaller<FibArgs> __Marshaller_FibArgs = Marshallers.Create((arg) => arg.ToByteArray(), FibArgs.ParseFrom); static readonly Marshaller<global::math.FibArgs> __Marshaller_FibArgs = Marshallers.Create((arg) => arg.ToByteArray(), global::math.FibArgs.ParseFrom);
static readonly Marshaller<Num> __Marshaller_Num = Marshallers.Create((arg) => arg.ToByteArray(), Num.ParseFrom); static readonly Marshaller<global::math.Num> __Marshaller_Num = Marshallers.Create((arg) => arg.ToByteArray(), global::math.Num.ParseFrom);
static readonly Method<DivArgs, DivReply> __Method_Div = new Method<DivArgs, DivReply>( static readonly Method<global::math.DivArgs, global::math.DivReply> __Method_Div = new Method<global::math.DivArgs, global::math.DivReply>(
MethodType.Unary, MethodType.Unary,
"Div", "Div",
__Marshaller_DivArgs, __Marshaller_DivArgs,
__Marshaller_DivReply); __Marshaller_DivReply);
static readonly Method<DivArgs, DivReply> __Method_DivMany = new Method<DivArgs, DivReply>( static readonly Method<global::math.DivArgs, global::math.DivReply> __Method_DivMany = new Method<global::math.DivArgs, global::math.DivReply>(
MethodType.DuplexStreaming, MethodType.DuplexStreaming,
"DivMany", "DivMany",
__Marshaller_DivArgs, __Marshaller_DivArgs,
__Marshaller_DivReply); __Marshaller_DivReply);
static readonly Method<FibArgs, Num> __Method_Fib = new Method<FibArgs, Num>( static readonly Method<global::math.FibArgs, global::math.Num> __Method_Fib = new Method<global::math.FibArgs, global::math.Num>(
MethodType.ServerStreaming, MethodType.ServerStreaming,
"Fib", "Fib",
__Marshaller_FibArgs, __Marshaller_FibArgs,
__Marshaller_Num); __Marshaller_Num);
static readonly Method<Num, Num> __Method_Sum = new Method<Num, Num>( static readonly Method<global::math.Num, global::math.Num> __Method_Sum = new Method<global::math.Num, global::math.Num>(
MethodType.ClientStreaming, MethodType.ClientStreaming,
"Sum", "Sum",
__Marshaller_Num, __Marshaller_Num,
@ -44,20 +44,20 @@ namespace math {
// client-side stub interface // client-side stub interface
public interface IMathClient public interface IMathClient
{ {
DivReply Div(DivArgs request, CancellationToken token = default(CancellationToken)); global::math.DivReply Div(global::math.DivArgs request, CancellationToken token = default(CancellationToken));
Task<DivReply> DivAsync(DivArgs request, CancellationToken token = default(CancellationToken)); Task<global::math.DivReply> DivAsync(global::math.DivArgs request, CancellationToken token = default(CancellationToken));
AsyncDuplexStreamingCall<DivArgs, DivReply> DivMany(CancellationToken token = default(CancellationToken)); AsyncDuplexStreamingCall<global::math.DivArgs, global::math.DivReply> DivMany(CancellationToken token = default(CancellationToken));
AsyncServerStreamingCall<Num> Fib(FibArgs request, CancellationToken token = default(CancellationToken)); AsyncServerStreamingCall<global::math.Num> Fib(global::math.FibArgs request, CancellationToken token = default(CancellationToken));
AsyncClientStreamingCall<Num, Num> Sum(CancellationToken token = default(CancellationToken)); AsyncClientStreamingCall<global::math.Num, global::math.Num> Sum(CancellationToken token = default(CancellationToken));
} }
// server-side interface // server-side interface
public interface IMath public interface IMath
{ {
Task<DivReply> Div(ServerCallContext context, DivArgs request); Task<global::math.DivReply> Div(ServerCallContext context, global::math.DivArgs request);
Task DivMany(ServerCallContext context, IAsyncStreamReader<DivArgs> requestStream, IServerStreamWriter<DivReply> responseStream); Task DivMany(ServerCallContext context, IAsyncStreamReader<global::math.DivArgs> requestStream, IServerStreamWriter<global::math.DivReply> responseStream);
Task Fib(ServerCallContext context, FibArgs request, IServerStreamWriter<Num> responseStream); Task Fib(ServerCallContext context, global::math.FibArgs request, IServerStreamWriter<global::math.Num> responseStream);
Task<Num> Sum(ServerCallContext context, IAsyncStreamReader<Num> requestStream); Task<global::math.Num> Sum(ServerCallContext context, IAsyncStreamReader<global::math.Num> requestStream);
} }
// client stub // client stub
@ -69,27 +69,27 @@ namespace math {
public MathClient(Channel channel, StubConfiguration config) : base(channel, config) public MathClient(Channel channel, StubConfiguration config) : base(channel, config)
{ {
} }
public DivReply Div(DivArgs request, CancellationToken token = default(CancellationToken)) public global::math.DivReply Div(global::math.DivArgs request, CancellationToken token = default(CancellationToken))
{ {
var call = CreateCall(__ServiceName, __Method_Div); var call = CreateCall(__ServiceName, __Method_Div);
return Calls.BlockingUnaryCall(call, request, token); return Calls.BlockingUnaryCall(call, request, token);
} }
public Task<DivReply> DivAsync(DivArgs request, CancellationToken token = default(CancellationToken)) public Task<global::math.DivReply> DivAsync(global::math.DivArgs request, CancellationToken token = default(CancellationToken))
{ {
var call = CreateCall(__ServiceName, __Method_Div); var call = CreateCall(__ServiceName, __Method_Div);
return Calls.AsyncUnaryCall(call, request, token); return Calls.AsyncUnaryCall(call, request, token);
} }
public AsyncDuplexStreamingCall<DivArgs, DivReply> DivMany(CancellationToken token = default(CancellationToken)) public AsyncDuplexStreamingCall<global::math.DivArgs, global::math.DivReply> DivMany(CancellationToken token = default(CancellationToken))
{ {
var call = CreateCall(__ServiceName, __Method_DivMany); var call = CreateCall(__ServiceName, __Method_DivMany);
return Calls.AsyncDuplexStreamingCall(call, token); return Calls.AsyncDuplexStreamingCall(call, token);
} }
public AsyncServerStreamingCall<Num> Fib(FibArgs request, CancellationToken token = default(CancellationToken)) public AsyncServerStreamingCall<global::math.Num> Fib(global::math.FibArgs request, CancellationToken token = default(CancellationToken))
{ {
var call = CreateCall(__ServiceName, __Method_Fib); var call = CreateCall(__ServiceName, __Method_Fib);
return Calls.AsyncServerStreamingCall(call, request, token); return Calls.AsyncServerStreamingCall(call, request, token);
} }
public AsyncClientStreamingCall<Num, Num> Sum(CancellationToken token = default(CancellationToken)) public AsyncClientStreamingCall<global::math.Num, global::math.Num> Sum(CancellationToken token = default(CancellationToken))
{ {
var call = CreateCall(__ServiceName, __Method_Sum); var call = CreateCall(__ServiceName, __Method_Sum);
return Calls.AsyncClientStreamingCall(call, token); return Calls.AsyncClientStreamingCall(call, token);

@ -62,7 +62,7 @@ namespace math
{ {
foreach (var num in FibInternal(request.Limit)) foreach (var num in FibInternal(request.Limit))
{ {
await responseStream.Write(num); await responseStream.WriteAsync(num);
} }
} }
} }
@ -81,7 +81,7 @@ namespace math
{ {
await requestStream.ForEach(async divArgs => await requestStream.ForEach(async divArgs =>
{ {
await responseStream.Write(DivInternal(divArgs)); await responseStream.WriteAsync(DivInternal(divArgs));
}); });
} }

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="Google.ProtocolBuffers" version="2.4.1.521" targetFramework="net45" /> <package id="Google.ProtocolBuffers" version="2.4.1.521" targetFramework="net45" />
<package id="Ix-Async" version="1.2.3" targetFramework="net45" />
<package id="NUnit" version="2.6.4" targetFramework="net45" /> <package id="NUnit" version="2.6.4" targetFramework="net45" />
</packages> </packages>

@ -54,6 +54,9 @@
<Reference Include="Google.ProtocolBuffers"> <Reference Include="Google.ProtocolBuffers">
<HintPath>..\packages\Google.ProtocolBuffers.2.4.1.521\lib\net40\Google.ProtocolBuffers.dll</HintPath> <HintPath>..\packages\Google.ProtocolBuffers.2.4.1.521\lib\net40\Google.ProtocolBuffers.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Interactive.Async">
<HintPath>..\packages\Ix-Async.1.2.3\lib\net45\System.Interactive.Async.dll</HintPath>
</Reference>
<Reference Include="System.Net" /> <Reference Include="System.Net" />
<Reference Include="System.Net.Http" /> <Reference Include="System.Net.Http" />
<Reference Include="System.Net.Http.Extensions"> <Reference Include="System.Net.Http.Extensions">

@ -213,11 +213,13 @@ namespace Grpc.IntegrationTesting
var bodySizes = new List<int> { 27182, 8, 1828, 45904 }.ConvertAll((size) => StreamingInputCallRequest.CreateBuilder().SetPayload(CreateZerosPayload(size)).Build()); var bodySizes = new List<int> { 27182, 8, 1828, 45904 }.ConvertAll((size) => StreamingInputCallRequest.CreateBuilder().SetPayload(CreateZerosPayload(size)).Build());
var call = client.StreamingInputCall(); using (var call = client.StreamingInputCall())
await call.RequestStream.WriteAll(bodySizes); {
await call.RequestStream.WriteAll(bodySizes);
var response = await call.Result; var response = await call.Result;
Assert.AreEqual(74922, response.AggregatedPayloadSize); Assert.AreEqual(74922, response.AggregatedPayloadSize);
}
Console.WriteLine("Passed!"); Console.WriteLine("Passed!");
}).Wait(); }).Wait();
} }
@ -236,14 +238,15 @@ namespace Grpc.IntegrationTesting
(size) => ResponseParameters.CreateBuilder().SetSize(size).Build())) (size) => ResponseParameters.CreateBuilder().SetSize(size).Build()))
.Build(); .Build();
var call = client.StreamingOutputCall(request); using (var call = client.StreamingOutputCall(request))
var responseList = await call.ResponseStream.ToList();
foreach (var res in responseList)
{ {
Assert.AreEqual(PayloadType.COMPRESSABLE, res.Payload.Type); var responseList = await call.ResponseStream.ToList();
foreach (var res in responseList)
{
Assert.AreEqual(PayloadType.COMPRESSABLE, res.Payload.Type);
}
CollectionAssert.AreEqual(bodySizes, responseList.ConvertAll((item) => item.Payload.Body.Length));
} }
CollectionAssert.AreEqual(bodySizes, responseList.ConvertAll((item) => item.Payload.Body.Length));
Console.WriteLine("Passed!"); Console.WriteLine("Passed!");
}).Wait(); }).Wait();
} }
@ -254,51 +257,48 @@ namespace Grpc.IntegrationTesting
{ {
Console.WriteLine("running ping_pong"); Console.WriteLine("running ping_pong");
var call = client.FullDuplexCall(); using (var call = client.FullDuplexCall())
{
StreamingOutputCallResponse response; await call.RequestStream.WriteAsync(StreamingOutputCallRequest.CreateBuilder()
.SetResponseType(PayloadType.COMPRESSABLE)
await call.RequestStream.Write(StreamingOutputCallRequest.CreateBuilder() .AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(31415))
.SetResponseType(PayloadType.COMPRESSABLE) .SetPayload(CreateZerosPayload(27182)).Build());
.AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(31415))
.SetPayload(CreateZerosPayload(27182)).Build());
response = await call.ResponseStream.ReadNext();
Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type);
Assert.AreEqual(31415, response.Payload.Body.Length);
await call.RequestStream.Write(StreamingOutputCallRequest.CreateBuilder() Assert.IsTrue(await call.ResponseStream.MoveNext());
.SetResponseType(PayloadType.COMPRESSABLE) Assert.AreEqual(PayloadType.COMPRESSABLE, call.ResponseStream.Current.Payload.Type);
.AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(9)) Assert.AreEqual(31415, call.ResponseStream.Current.Payload.Body.Length);
.SetPayload(CreateZerosPayload(8)).Build());
response = await call.ResponseStream.ReadNext(); await call.RequestStream.WriteAsync(StreamingOutputCallRequest.CreateBuilder()
Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type); .SetResponseType(PayloadType.COMPRESSABLE)
Assert.AreEqual(9, response.Payload.Body.Length); .AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(9))
.SetPayload(CreateZerosPayload(8)).Build());
await call.RequestStream.Write(StreamingOutputCallRequest.CreateBuilder() Assert.IsTrue(await call.ResponseStream.MoveNext());
.SetResponseType(PayloadType.COMPRESSABLE) Assert.AreEqual(PayloadType.COMPRESSABLE, call.ResponseStream.Current.Payload.Type);
.AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(2653)) Assert.AreEqual(9, call.ResponseStream.Current.Payload.Body.Length);
.SetPayload(CreateZerosPayload(1828)).Build());
response = await call.ResponseStream.ReadNext(); await call.RequestStream.WriteAsync(StreamingOutputCallRequest.CreateBuilder()
Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type); .SetResponseType(PayloadType.COMPRESSABLE)
Assert.AreEqual(2653, response.Payload.Body.Length); .AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(2653))
.SetPayload(CreateZerosPayload(1828)).Build());
await call.RequestStream.Write(StreamingOutputCallRequest.CreateBuilder() Assert.IsTrue(await call.ResponseStream.MoveNext());
.SetResponseType(PayloadType.COMPRESSABLE) Assert.AreEqual(PayloadType.COMPRESSABLE, call.ResponseStream.Current.Payload.Type);
.AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(58979)) Assert.AreEqual(2653, call.ResponseStream.Current.Payload.Body.Length);
.SetPayload(CreateZerosPayload(45904)).Build());
response = await call.ResponseStream.ReadNext(); await call.RequestStream.WriteAsync(StreamingOutputCallRequest.CreateBuilder()
Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type); .SetResponseType(PayloadType.COMPRESSABLE)
Assert.AreEqual(58979, response.Payload.Body.Length); .AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(58979))
.SetPayload(CreateZerosPayload(45904)).Build());
await call.RequestStream.Close(); Assert.IsTrue(await call.ResponseStream.MoveNext());
Assert.AreEqual(PayloadType.COMPRESSABLE, call.ResponseStream.Current.Payload.Type);
Assert.AreEqual(58979, call.ResponseStream.Current.Payload.Body.Length);
response = await call.ResponseStream.ReadNext(); await call.RequestStream.CompleteAsync();
Assert.AreEqual(null, response);
Assert.IsFalse(await call.ResponseStream.MoveNext());
}
Console.WriteLine("Passed!"); Console.WriteLine("Passed!");
}).Wait(); }).Wait();
} }
@ -308,12 +308,13 @@ namespace Grpc.IntegrationTesting
Task.Run(async () => Task.Run(async () =>
{ {
Console.WriteLine("running empty_stream"); Console.WriteLine("running empty_stream");
var call = client.FullDuplexCall(); using (var call = client.FullDuplexCall())
await call.Close(); {
await call.RequestStream.CompleteAsync();
var responseList = await call.ResponseStream.ToList();
Assert.AreEqual(0, responseList.Count);
var responseList = await call.ResponseStream.ToList();
Assert.AreEqual(0, responseList.Count);
}
Console.WriteLine("Passed!"); Console.WriteLine("Passed!");
}).Wait(); }).Wait();
} }
@ -365,19 +366,21 @@ namespace Grpc.IntegrationTesting
Console.WriteLine("running cancel_after_begin"); Console.WriteLine("running cancel_after_begin");
var cts = new CancellationTokenSource(); var cts = new CancellationTokenSource();
var call = client.StreamingInputCall(cts.Token); using (var call = client.StreamingInputCall(cts.Token))
// TODO(jtattermusch): we need this to ensure call has been initiated once we cancel it.
await Task.Delay(1000);
cts.Cancel();
try
{ {
var response = await call.Result; // TODO(jtattermusch): we need this to ensure call has been initiated once we cancel it.
Assert.Fail(); await Task.Delay(1000);
} cts.Cancel();
catch (RpcException e)
{ try
Assert.AreEqual(StatusCode.Cancelled, e.Status.StatusCode); {
var response = await call.Result;
Assert.Fail();
}
catch (RpcException e)
{
Assert.AreEqual(StatusCode.Cancelled, e.Status.StatusCode);
}
} }
Console.WriteLine("Passed!"); Console.WriteLine("Passed!");
}).Wait(); }).Wait();
@ -390,29 +393,28 @@ namespace Grpc.IntegrationTesting
Console.WriteLine("running cancel_after_first_response"); Console.WriteLine("running cancel_after_first_response");
var cts = new CancellationTokenSource(); var cts = new CancellationTokenSource();
var call = client.FullDuplexCall(cts.Token); using (var call = client.FullDuplexCall(cts.Token))
{
StreamingOutputCallResponse response; await call.RequestStream.WriteAsync(StreamingOutputCallRequest.CreateBuilder()
.SetResponseType(PayloadType.COMPRESSABLE)
await call.RequestStream.Write(StreamingOutputCallRequest.CreateBuilder() .AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(31415))
.SetResponseType(PayloadType.COMPRESSABLE) .SetPayload(CreateZerosPayload(27182)).Build());
.AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(31415))
.SetPayload(CreateZerosPayload(27182)).Build());
response = await call.ResponseStream.ReadNext(); Assert.IsTrue(await call.ResponseStream.MoveNext());
Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type); Assert.AreEqual(PayloadType.COMPRESSABLE, call.ResponseStream.Current.Payload.Type);
Assert.AreEqual(31415, response.Payload.Body.Length); Assert.AreEqual(31415, call.ResponseStream.Current.Payload.Body.Length);
cts.Cancel(); cts.Cancel();
try try
{ {
response = await call.ResponseStream.ReadNext(); await call.ResponseStream.MoveNext();
Assert.Fail(); Assert.Fail();
} }
catch (RpcException e) catch (RpcException e)
{ {
Assert.AreEqual(StatusCode.Cancelled, e.Status.StatusCode); Assert.AreEqual(StatusCode.Cancelled, e.Status.StatusCode);
}
} }
Console.WriteLine("Passed!"); Console.WriteLine("Passed!");
}).Wait(); }).Wait();

@ -12,45 +12,45 @@ namespace grpc.testing {
{ {
static readonly string __ServiceName = "grpc.testing.TestService"; static readonly string __ServiceName = "grpc.testing.TestService";
static readonly Marshaller<Empty> __Marshaller_Empty = Marshallers.Create((arg) => arg.ToByteArray(), Empty.ParseFrom); static readonly Marshaller<global::grpc.testing.Empty> __Marshaller_Empty = Marshallers.Create((arg) => arg.ToByteArray(), global::grpc.testing.Empty.ParseFrom);
static readonly Marshaller<SimpleRequest> __Marshaller_SimpleRequest = Marshallers.Create((arg) => arg.ToByteArray(), SimpleRequest.ParseFrom); static readonly Marshaller<global::grpc.testing.SimpleRequest> __Marshaller_SimpleRequest = Marshallers.Create((arg) => arg.ToByteArray(), global::grpc.testing.SimpleRequest.ParseFrom);
static readonly Marshaller<SimpleResponse> __Marshaller_SimpleResponse = Marshallers.Create((arg) => arg.ToByteArray(), SimpleResponse.ParseFrom); static readonly Marshaller<global::grpc.testing.SimpleResponse> __Marshaller_SimpleResponse = Marshallers.Create((arg) => arg.ToByteArray(), global::grpc.testing.SimpleResponse.ParseFrom);
static readonly Marshaller<StreamingOutputCallRequest> __Marshaller_StreamingOutputCallRequest = Marshallers.Create((arg) => arg.ToByteArray(), StreamingOutputCallRequest.ParseFrom); static readonly Marshaller<global::grpc.testing.StreamingOutputCallRequest> __Marshaller_StreamingOutputCallRequest = Marshallers.Create((arg) => arg.ToByteArray(), global::grpc.testing.StreamingOutputCallRequest.ParseFrom);
static readonly Marshaller<StreamingOutputCallResponse> __Marshaller_StreamingOutputCallResponse = Marshallers.Create((arg) => arg.ToByteArray(), StreamingOutputCallResponse.ParseFrom); static readonly Marshaller<global::grpc.testing.StreamingOutputCallResponse> __Marshaller_StreamingOutputCallResponse = Marshallers.Create((arg) => arg.ToByteArray(), global::grpc.testing.StreamingOutputCallResponse.ParseFrom);
static readonly Marshaller<StreamingInputCallRequest> __Marshaller_StreamingInputCallRequest = Marshallers.Create((arg) => arg.ToByteArray(), StreamingInputCallRequest.ParseFrom); static readonly Marshaller<global::grpc.testing.StreamingInputCallRequest> __Marshaller_StreamingInputCallRequest = Marshallers.Create((arg) => arg.ToByteArray(), global::grpc.testing.StreamingInputCallRequest.ParseFrom);
static readonly Marshaller<StreamingInputCallResponse> __Marshaller_StreamingInputCallResponse = Marshallers.Create((arg) => arg.ToByteArray(), StreamingInputCallResponse.ParseFrom); static readonly Marshaller<global::grpc.testing.StreamingInputCallResponse> __Marshaller_StreamingInputCallResponse = Marshallers.Create((arg) => arg.ToByteArray(), global::grpc.testing.StreamingInputCallResponse.ParseFrom);
static readonly Method<Empty, Empty> __Method_EmptyCall = new Method<Empty, Empty>( static readonly Method<global::grpc.testing.Empty, global::grpc.testing.Empty> __Method_EmptyCall = new Method<global::grpc.testing.Empty, global::grpc.testing.Empty>(
MethodType.Unary, MethodType.Unary,
"EmptyCall", "EmptyCall",
__Marshaller_Empty, __Marshaller_Empty,
__Marshaller_Empty); __Marshaller_Empty);
static readonly Method<SimpleRequest, SimpleResponse> __Method_UnaryCall = new Method<SimpleRequest, SimpleResponse>( static readonly Method<global::grpc.testing.SimpleRequest, global::grpc.testing.SimpleResponse> __Method_UnaryCall = new Method<global::grpc.testing.SimpleRequest, global::grpc.testing.SimpleResponse>(
MethodType.Unary, MethodType.Unary,
"UnaryCall", "UnaryCall",
__Marshaller_SimpleRequest, __Marshaller_SimpleRequest,
__Marshaller_SimpleResponse); __Marshaller_SimpleResponse);
static readonly Method<StreamingOutputCallRequest, StreamingOutputCallResponse> __Method_StreamingOutputCall = new Method<StreamingOutputCallRequest, StreamingOutputCallResponse>( static readonly Method<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse> __Method_StreamingOutputCall = new Method<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse>(
MethodType.ServerStreaming, MethodType.ServerStreaming,
"StreamingOutputCall", "StreamingOutputCall",
__Marshaller_StreamingOutputCallRequest, __Marshaller_StreamingOutputCallRequest,
__Marshaller_StreamingOutputCallResponse); __Marshaller_StreamingOutputCallResponse);
static readonly Method<StreamingInputCallRequest, StreamingInputCallResponse> __Method_StreamingInputCall = new Method<StreamingInputCallRequest, StreamingInputCallResponse>( static readonly Method<global::grpc.testing.StreamingInputCallRequest, global::grpc.testing.StreamingInputCallResponse> __Method_StreamingInputCall = new Method<global::grpc.testing.StreamingInputCallRequest, global::grpc.testing.StreamingInputCallResponse>(
MethodType.ClientStreaming, MethodType.ClientStreaming,
"StreamingInputCall", "StreamingInputCall",
__Marshaller_StreamingInputCallRequest, __Marshaller_StreamingInputCallRequest,
__Marshaller_StreamingInputCallResponse); __Marshaller_StreamingInputCallResponse);
static readonly Method<StreamingOutputCallRequest, StreamingOutputCallResponse> __Method_FullDuplexCall = new Method<StreamingOutputCallRequest, StreamingOutputCallResponse>( static readonly Method<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse> __Method_FullDuplexCall = new Method<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse>(
MethodType.DuplexStreaming, MethodType.DuplexStreaming,
"FullDuplexCall", "FullDuplexCall",
__Marshaller_StreamingOutputCallRequest, __Marshaller_StreamingOutputCallRequest,
__Marshaller_StreamingOutputCallResponse); __Marshaller_StreamingOutputCallResponse);
static readonly Method<StreamingOutputCallRequest, StreamingOutputCallResponse> __Method_HalfDuplexCall = new Method<StreamingOutputCallRequest, StreamingOutputCallResponse>( static readonly Method<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse> __Method_HalfDuplexCall = new Method<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse>(
MethodType.DuplexStreaming, MethodType.DuplexStreaming,
"HalfDuplexCall", "HalfDuplexCall",
__Marshaller_StreamingOutputCallRequest, __Marshaller_StreamingOutputCallRequest,
@ -59,25 +59,25 @@ namespace grpc.testing {
// client-side stub interface // client-side stub interface
public interface ITestServiceClient public interface ITestServiceClient
{ {
Empty EmptyCall(Empty request, CancellationToken token = default(CancellationToken)); global::grpc.testing.Empty EmptyCall(global::grpc.testing.Empty request, CancellationToken token = default(CancellationToken));
Task<Empty> EmptyCallAsync(Empty request, CancellationToken token = default(CancellationToken)); Task<global::grpc.testing.Empty> EmptyCallAsync(global::grpc.testing.Empty request, CancellationToken token = default(CancellationToken));
SimpleResponse UnaryCall(SimpleRequest request, CancellationToken token = default(CancellationToken)); global::grpc.testing.SimpleResponse UnaryCall(global::grpc.testing.SimpleRequest request, CancellationToken token = default(CancellationToken));
Task<SimpleResponse> UnaryCallAsync(SimpleRequest request, CancellationToken token = default(CancellationToken)); Task<global::grpc.testing.SimpleResponse> UnaryCallAsync(global::grpc.testing.SimpleRequest request, CancellationToken token = default(CancellationToken));
AsyncServerStreamingCall<StreamingOutputCallResponse> StreamingOutputCall(StreamingOutputCallRequest request, CancellationToken token = default(CancellationToken)); AsyncServerStreamingCall<global::grpc.testing.StreamingOutputCallResponse> StreamingOutputCall(global::grpc.testing.StreamingOutputCallRequest request, CancellationToken token = default(CancellationToken));
AsyncClientStreamingCall<StreamingInputCallRequest, StreamingInputCallResponse> StreamingInputCall(CancellationToken token = default(CancellationToken)); AsyncClientStreamingCall<global::grpc.testing.StreamingInputCallRequest, global::grpc.testing.StreamingInputCallResponse> StreamingInputCall(CancellationToken token = default(CancellationToken));
AsyncDuplexStreamingCall<StreamingOutputCallRequest, StreamingOutputCallResponse> FullDuplexCall(CancellationToken token = default(CancellationToken)); AsyncDuplexStreamingCall<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse> FullDuplexCall(CancellationToken token = default(CancellationToken));
AsyncDuplexStreamingCall<StreamingOutputCallRequest, StreamingOutputCallResponse> HalfDuplexCall(CancellationToken token = default(CancellationToken)); AsyncDuplexStreamingCall<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse> HalfDuplexCall(CancellationToken token = default(CancellationToken));
} }
// server-side interface // server-side interface
public interface ITestService public interface ITestService
{ {
Task<Empty> EmptyCall(ServerCallContext context, Empty request); Task<global::grpc.testing.Empty> EmptyCall(ServerCallContext context, global::grpc.testing.Empty request);
Task<SimpleResponse> UnaryCall(ServerCallContext context, SimpleRequest request); Task<global::grpc.testing.SimpleResponse> UnaryCall(ServerCallContext context, global::grpc.testing.SimpleRequest request);
Task StreamingOutputCall(ServerCallContext context, StreamingOutputCallRequest request, IServerStreamWriter<StreamingOutputCallResponse> responseStream); Task StreamingOutputCall(ServerCallContext context, global::grpc.testing.StreamingOutputCallRequest request, IServerStreamWriter<global::grpc.testing.StreamingOutputCallResponse> responseStream);
Task<StreamingInputCallResponse> StreamingInputCall(ServerCallContext context, IAsyncStreamReader<StreamingInputCallRequest> requestStream); Task<global::grpc.testing.StreamingInputCallResponse> StreamingInputCall(ServerCallContext context, IAsyncStreamReader<global::grpc.testing.StreamingInputCallRequest> requestStream);
Task FullDuplexCall(ServerCallContext context, IAsyncStreamReader<StreamingOutputCallRequest> requestStream, IServerStreamWriter<StreamingOutputCallResponse> responseStream); Task FullDuplexCall(ServerCallContext context, IAsyncStreamReader<global::grpc.testing.StreamingOutputCallRequest> requestStream, IServerStreamWriter<global::grpc.testing.StreamingOutputCallResponse> responseStream);
Task HalfDuplexCall(ServerCallContext context, IAsyncStreamReader<StreamingOutputCallRequest> requestStream, IServerStreamWriter<StreamingOutputCallResponse> responseStream); Task HalfDuplexCall(ServerCallContext context, IAsyncStreamReader<global::grpc.testing.StreamingOutputCallRequest> requestStream, IServerStreamWriter<global::grpc.testing.StreamingOutputCallResponse> responseStream);
} }
// client stub // client stub
@ -89,42 +89,42 @@ namespace grpc.testing {
public TestServiceClient(Channel channel, StubConfiguration config) : base(channel, config) public TestServiceClient(Channel channel, StubConfiguration config) : base(channel, config)
{ {
} }
public Empty EmptyCall(Empty request, CancellationToken token = default(CancellationToken)) public global::grpc.testing.Empty EmptyCall(global::grpc.testing.Empty request, CancellationToken token = default(CancellationToken))
{ {
var call = CreateCall(__ServiceName, __Method_EmptyCall); var call = CreateCall(__ServiceName, __Method_EmptyCall);
return Calls.BlockingUnaryCall(call, request, token); return Calls.BlockingUnaryCall(call, request, token);
} }
public Task<Empty> EmptyCallAsync(Empty request, CancellationToken token = default(CancellationToken)) public Task<global::grpc.testing.Empty> EmptyCallAsync(global::grpc.testing.Empty request, CancellationToken token = default(CancellationToken))
{ {
var call = CreateCall(__ServiceName, __Method_EmptyCall); var call = CreateCall(__ServiceName, __Method_EmptyCall);
return Calls.AsyncUnaryCall(call, request, token); return Calls.AsyncUnaryCall(call, request, token);
} }
public SimpleResponse UnaryCall(SimpleRequest request, CancellationToken token = default(CancellationToken)) public global::grpc.testing.SimpleResponse UnaryCall(global::grpc.testing.SimpleRequest request, CancellationToken token = default(CancellationToken))
{ {
var call = CreateCall(__ServiceName, __Method_UnaryCall); var call = CreateCall(__ServiceName, __Method_UnaryCall);
return Calls.BlockingUnaryCall(call, request, token); return Calls.BlockingUnaryCall(call, request, token);
} }
public Task<SimpleResponse> UnaryCallAsync(SimpleRequest request, CancellationToken token = default(CancellationToken)) public Task<global::grpc.testing.SimpleResponse> UnaryCallAsync(global::grpc.testing.SimpleRequest request, CancellationToken token = default(CancellationToken))
{ {
var call = CreateCall(__ServiceName, __Method_UnaryCall); var call = CreateCall(__ServiceName, __Method_UnaryCall);
return Calls.AsyncUnaryCall(call, request, token); return Calls.AsyncUnaryCall(call, request, token);
} }
public AsyncServerStreamingCall<StreamingOutputCallResponse> StreamingOutputCall(StreamingOutputCallRequest request, CancellationToken token = default(CancellationToken)) public AsyncServerStreamingCall<global::grpc.testing.StreamingOutputCallResponse> StreamingOutputCall(global::grpc.testing.StreamingOutputCallRequest request, CancellationToken token = default(CancellationToken))
{ {
var call = CreateCall(__ServiceName, __Method_StreamingOutputCall); var call = CreateCall(__ServiceName, __Method_StreamingOutputCall);
return Calls.AsyncServerStreamingCall(call, request, token); return Calls.AsyncServerStreamingCall(call, request, token);
} }
public AsyncClientStreamingCall<StreamingInputCallRequest, StreamingInputCallResponse> StreamingInputCall(CancellationToken token = default(CancellationToken)) public AsyncClientStreamingCall<global::grpc.testing.StreamingInputCallRequest, global::grpc.testing.StreamingInputCallResponse> StreamingInputCall(CancellationToken token = default(CancellationToken))
{ {
var call = CreateCall(__ServiceName, __Method_StreamingInputCall); var call = CreateCall(__ServiceName, __Method_StreamingInputCall);
return Calls.AsyncClientStreamingCall(call, token); return Calls.AsyncClientStreamingCall(call, token);
} }
public AsyncDuplexStreamingCall<StreamingOutputCallRequest, StreamingOutputCallResponse> FullDuplexCall(CancellationToken token = default(CancellationToken)) public AsyncDuplexStreamingCall<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse> FullDuplexCall(CancellationToken token = default(CancellationToken))
{ {
var call = CreateCall(__ServiceName, __Method_FullDuplexCall); var call = CreateCall(__ServiceName, __Method_FullDuplexCall);
return Calls.AsyncDuplexStreamingCall(call, token); return Calls.AsyncDuplexStreamingCall(call, token);
} }
public AsyncDuplexStreamingCall<StreamingOutputCallRequest, StreamingOutputCallResponse> HalfDuplexCall(CancellationToken token = default(CancellationToken)) public AsyncDuplexStreamingCall<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse> HalfDuplexCall(CancellationToken token = default(CancellationToken))
{ {
var call = CreateCall(__ServiceName, __Method_HalfDuplexCall); var call = CreateCall(__ServiceName, __Method_HalfDuplexCall);
return Calls.AsyncDuplexStreamingCall(call, token); return Calls.AsyncDuplexStreamingCall(call, token);

@ -64,7 +64,7 @@ namespace grpc.testing
{ {
var response = StreamingOutputCallResponse.CreateBuilder() var response = StreamingOutputCallResponse.CreateBuilder()
.SetPayload(CreateZerosPayload(responseParam.Size)).Build(); .SetPayload(CreateZerosPayload(responseParam.Size)).Build();
await responseStream.Write(response); await responseStream.WriteAsync(response);
} }
} }
@ -86,7 +86,7 @@ namespace grpc.testing
{ {
var response = StreamingOutputCallResponse.CreateBuilder() var response = StreamingOutputCallResponse.CreateBuilder()
.SetPayload(CreateZerosPayload(responseParam.Size)).Build(); .SetPayload(CreateZerosPayload(responseParam.Size)).Build();
await responseStream.Write(response); await responseStream.WriteAsync(response);
} }
}); });
} }

@ -3,6 +3,7 @@
<package id="Google.Apis.Auth" version="1.9.1" targetFramework="net45" /> <package id="Google.Apis.Auth" version="1.9.1" targetFramework="net45" />
<package id="Google.Apis.Core" version="1.9.1" targetFramework="net45" /> <package id="Google.Apis.Core" version="1.9.1" targetFramework="net45" />
<package id="Google.ProtocolBuffers" version="2.4.1.521" targetFramework="net45" /> <package id="Google.ProtocolBuffers" version="2.4.1.521" targetFramework="net45" />
<package id="Ix-Async" version="1.2.3" targetFramework="net45" />
<package id="Microsoft.Bcl" version="1.1.9" targetFramework="net45" /> <package id="Microsoft.Bcl" version="1.1.9" targetFramework="net45" />
<package id="Microsoft.Bcl.Async" version="1.0.168" targetFramework="net45" /> <package id="Microsoft.Bcl.Async" version="1.0.168" targetFramework="net45" />
<package id="Microsoft.Bcl.Build" version="1.0.14" targetFramework="net45" /> <package id="Microsoft.Bcl.Build" version="1.0.14" targetFramework="net45" />

@ -11,8 +11,8 @@ endlocal
@call buildall.bat || goto :error @call buildall.bat || goto :error
%NUGET% pack ..\..\vsprojects\nuget_package\grpc.native.csharp_ext.nuspec || goto :error %NUGET% pack ..\..\vsprojects\nuget_package\grpc.native.csharp_ext.nuspec || goto :error
%NUGET% pack Grpc.Core\Grpc.Core.nuspec || goto :error %NUGET% pack Grpc.Core\Grpc.Core.nuspec -Symbols || goto :error
%NUGET% pack Grpc.Auth\Grpc.Auth.nuspec || goto :error %NUGET% pack Grpc.Auth\Grpc.Auth.nuspec -Symbols || goto :error
%NUGET% pack Grpc.nuspec || goto :error %NUGET% pack Grpc.nuspec || goto :error
goto :EOF goto :EOF

@ -0,0 +1,16 @@
# Command Line Tools
# Service Packager
The command line tool `bin/service_packager`, when called with the following command line:
```bash
service_packager proto_file -o output_path -n name -v version [-i input_path...]
```
Populates `output_path` with a node package consisting of a `package.json` populated with `name` and `version`, an `index.js`, a `LICENSE` file copied from gRPC, and a `service.json`, which is compiled from `proto_file` and the given `input_path`s. `require('output_path')` returns an object that is equivalent to
```js
{ client: require('grpc').load('service.json'),
auth: require('google-auth-library') }
```

@ -0,0 +1,2 @@
#!/usr/bin/env node
require(__dirname+'/../cli/service_packager.js').main(process.argv.slice(2));

@ -0,0 +1,142 @@
/*
*
* 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.
*
*/
'use strict';
var fs = require('fs');
var path = require('path');
var _ = require('underscore');
var async = require('async');
var pbjs = require('protobufjs/cli/pbjs');
var parseArgs = require('minimist');
var Mustache = require('mustache');
var package_json = require('../package.json');
var template_path = path.resolve(__dirname, 'service_packager');
var package_tpl_path = path.join(template_path, 'package.json.template');
var arg_format = {
string: ['include', 'out', 'name', 'version'],
alias: {
include: 'i',
out: 'o',
name: 'n',
version: 'v'
}
};
// TODO(mlumish): autogenerate README.md from proto file
/**
* Render package.json file from template using provided parameters.
* @param {Object} params Map of parameter names to values
* @param {function(Error, string)} callback Callback to pass rendered template
* text to
*/
function generatePackage(params, callback) {
fs.readFile(package_tpl_path, {encoding: 'utf-8'}, function(err, template) {
if (err) {
callback(err);
} else {
var rendered = Mustache.render(template, params);
callback(null, rendered);
}
});
}
/**
* Copy a file
* @param {string} src_path The filepath to copy from
* @param {string} dest_path The filepath to copy to
*/
function copyFile(src_path, dest_path) {
fs.createReadStream(src_path).pipe(fs.createWriteStream(dest_path));
}
/**
* Run the script. Copies the index.js and LICENSE files to the output path,
* renders the package.json template to the output path, and generates a
* service.json file from the input proto files using pbjs. The arguments are
* taken directly from the command line, and handled as follows:
* -i (--include) : An include path for pbjs (can be dpulicated)
* -o (--output): The output path
* -n (--name): The name of the package
* -v (--version): The package version
* @param {Array} argv The argument vector
*/
function main(argv) {
var args = parseArgs(argv, arg_format);
var out_path = path.resolve(args.out);
var include_dirs = [];
if (args.include) {
include_dirs = _.map(_.flatten([args.include]), function(p) {
return path.resolve(p);
});
}
args.grpc_version = package_json.version;
generatePackage(args, function(err, rendered) {
if (err) throw err;
fs.writeFile(path.join(out_path, 'package.json'), rendered, function(err) {
if (err) throw err;
});
});
copyFile(path.join(template_path, 'index.js'),
path.join(out_path, 'index.js'));
copyFile(path.join(__dirname, '..', 'LICENSE'),
path.join(out_path, 'LICENSE'));
var service_stream = fs.createWriteStream(path.join(out_path,
'service.json'));
var pbjs_args = _.flatten(['node', 'pbjs',
args._[0],
'-legacy',
_.map(include_dirs, function(dir) {
return "-path=" + dir;
})]);
var old_stdout = process.stdout;
process.__defineGetter__('stdout', function() {
return service_stream;
});
var pbjs_status = pbjs.main(pbjs_args);
process.__defineGetter__('stdout', function() {
return old_stdout;
});
if (pbjs_status !== pbjs.STATUS_OK) {
throw new Error('pbjs failed with status code ' + pbjs_status);
}
}
exports.main = main;

@ -0,0 +1,36 @@
/*
*
* 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.
*
*/
var grpc = require('grpc');
exports.client = grpc.load(__dirname + '/service.json', 'json');
exports.auth = require('google-auth-library');

@ -0,0 +1,17 @@
{
"name": "{{{name}}}",
"version": "{{{version}}}",
"author": "Google Inc.",
"description": "Client library for {{{name}}} built on gRPC",
"license": "Apache-2.0",
"dependencies": {
"grpc": "{{{grpc_version}}}",
"google-auth-library": "^0.9.2"
},
"main": "index.js",
"files": [
"LICENSE",
"index.js",
"service.json"
]
}

@ -154,13 +154,15 @@ function serverStreaming(client, done) {
arg.response_parameters[resp_index].size); arg.response_parameters[resp_index].size);
resp_index += 1; resp_index += 1;
}); });
call.on('status', function(status) { call.on('end', function() {
assert.strictEqual(status.code, grpc.status.OK);
assert.strictEqual(resp_index, 4); assert.strictEqual(resp_index, 4);
if (done) { if (done) {
done(); done();
} }
}); });
call.on('status', function(status) {
assert.strictEqual(status.code, grpc.status.OK);
});
} }
/** /**

@ -36,6 +36,7 @@
"jshint": "^2.5.0", "jshint": "^2.5.0",
"minimist": "^1.1.0", "minimist": "^1.1.0",
"mocha": "~1.21.0", "mocha": "~1.21.0",
"mustache": "^2.0.0",
"strftime": "^0.8.2" "strftime": "^0.8.2"
}, },
"engines": { "engines": {
@ -46,6 +47,8 @@
"README.md", "README.md",
"index.js", "index.js",
"binding.gyp", "binding.gyp",
"bin",
"cli",
"examples", "examples",
"ext", "ext",
"interop", "interop",

@ -81,7 +81,8 @@ function _write(chunk, encoding, callback) {
batch[grpc.opType.SEND_MESSAGE] = this.serialize(chunk); batch[grpc.opType.SEND_MESSAGE] = this.serialize(chunk);
this.call.startBatch(batch, function(err, event) { this.call.startBatch(batch, function(err, event) {
if (err) { if (err) {
throw err; // Something has gone wrong. Stop writing by failing to call callback
return;
} }
callback(); callback();
}); });
@ -120,10 +121,8 @@ function _read(size) {
*/ */
function readCallback(err, event) { function readCallback(err, event) {
if (err) { if (err) {
throw err; // Something has gone wrong. Stop reading and wait for status
} self.finished = true;
if (self.finished) {
self.push(null);
return; return;
} }
var data = event.read; var data = event.read;
@ -237,10 +236,6 @@ function makeUnaryRequestFunction(method, serialize, deserialize) {
client_batch[grpc.opType.RECV_MESSAGE] = true; client_batch[grpc.opType.RECV_MESSAGE] = true;
client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true;
call.startBatch(client_batch, function(err, response) { call.startBatch(client_batch, function(err, response) {
if (err) {
callback(err);
return;
}
emitter.emit('status', response.status); emitter.emit('status', response.status);
if (response.status.code !== grpc.status.OK) { if (response.status.code !== grpc.status.OK) {
var error = new Error(response.status.details); var error = new Error(response.status.details);
@ -248,6 +243,12 @@ function makeUnaryRequestFunction(method, serialize, deserialize) {
error.metadata = response.status.metadata; error.metadata = response.status.metadata;
callback(error); callback(error);
return; return;
} else {
if (err) {
// Got a batch error, but OK status. Something went wrong
callback(err);
return;
}
} }
emitter.emit('metadata', response.metadata); emitter.emit('metadata', response.metadata);
callback(null, deserialize(response.read)); callback(null, deserialize(response.read));
@ -300,7 +301,8 @@ function makeClientStreamRequestFunction(method, serialize, deserialize) {
metadata_batch[grpc.opType.RECV_INITIAL_METADATA] = true; metadata_batch[grpc.opType.RECV_INITIAL_METADATA] = true;
call.startBatch(metadata_batch, function(err, response) { call.startBatch(metadata_batch, function(err, response) {
if (err) { if (err) {
callback(err); // The call has stopped for some reason. A non-OK status will arrive
// in the other batch.
return; return;
} }
stream.emit('metadata', response.metadata); stream.emit('metadata', response.metadata);
@ -309,10 +311,6 @@ function makeClientStreamRequestFunction(method, serialize, deserialize) {
client_batch[grpc.opType.RECV_MESSAGE] = true; client_batch[grpc.opType.RECV_MESSAGE] = true;
client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true;
call.startBatch(client_batch, function(err, response) { call.startBatch(client_batch, function(err, response) {
if (err) {
callback(err);
return;
}
stream.emit('status', response.status); stream.emit('status', response.status);
if (response.status.code !== grpc.status.OK) { if (response.status.code !== grpc.status.OK) {
var error = new Error(response.status.details); var error = new Error(response.status.details);
@ -320,6 +318,12 @@ function makeClientStreamRequestFunction(method, serialize, deserialize) {
error.metadata = response.status.metadata; error.metadata = response.status.metadata;
callback(error); callback(error);
return; return;
} else {
if (err) {
// Got a batch error, but OK status. Something went wrong
callback(err);
return;
}
} }
callback(null, deserialize(response.read)); callback(null, deserialize(response.read));
}); });
@ -373,16 +377,15 @@ function makeServerStreamRequestFunction(method, serialize, deserialize) {
start_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; start_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true;
call.startBatch(start_batch, function(err, response) { call.startBatch(start_batch, function(err, response) {
if (err) { if (err) {
throw err; // The call has stopped for some reason. A non-OK status will arrive
// in the other batch.
return;
} }
stream.emit('metadata', response.metadata); stream.emit('metadata', response.metadata);
}); });
var status_batch = {}; var status_batch = {};
status_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; status_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true;
call.startBatch(status_batch, function(err, response) { call.startBatch(status_batch, function(err, response) {
if (err) {
throw err;
}
stream.emit('status', response.status); stream.emit('status', response.status);
if (response.status.code !== grpc.status.OK) { if (response.status.code !== grpc.status.OK) {
var error = new Error(response.status.details); var error = new Error(response.status.details);
@ -390,6 +393,12 @@ function makeServerStreamRequestFunction(method, serialize, deserialize) {
error.metadata = response.status.metadata; error.metadata = response.status.metadata;
stream.emit('error', error); stream.emit('error', error);
return; return;
} else {
if (err) {
// Got a batch error, but OK status. Something went wrong
stream.emit('error', err);
return;
}
} }
}); });
}); });
@ -438,16 +447,15 @@ function makeBidiStreamRequestFunction(method, serialize, deserialize) {
start_batch[grpc.opType.RECV_INITIAL_METADATA] = true; start_batch[grpc.opType.RECV_INITIAL_METADATA] = true;
call.startBatch(start_batch, function(err, response) { call.startBatch(start_batch, function(err, response) {
if (err) { if (err) {
throw err; // The call has stopped for some reason. A non-OK status will arrive
// in the other batch.
return;
} }
stream.emit('metadata', response.metadata); stream.emit('metadata', response.metadata);
}); });
var status_batch = {}; var status_batch = {};
status_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; status_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true;
call.startBatch(status_batch, function(err, response) { call.startBatch(status_batch, function(err, response) {
if (err) {
throw err;
}
stream.emit('status', response.status); stream.emit('status', response.status);
if (response.status.code !== grpc.status.OK) { if (response.status.code !== grpc.status.OK) {
var error = new Error(response.status.details); var error = new Error(response.status.details);
@ -455,6 +463,12 @@ function makeBidiStreamRequestFunction(method, serialize, deserialize) {
error.metadata = response.status.metadata; error.metadata = response.status.metadata;
stream.emit('error', error); stream.emit('error', error);
return; return;
} else {
if (err) {
// Got a batch error, but OK status. Something went wrong
stream.emit('error', err);
return;
}
} }
}); });
}); });

@ -291,7 +291,15 @@ function _read(size) {
return; return;
} }
var data = event.read; var data = event.read;
if (self.push(self.deserialize(data)) && data !== null) { var deserialized;
try {
deserialized = self.deserialize(data);
} catch (e) {
e.code = grpc.status.INVALID_ARGUMENT;
self.emit('error', e);
return;
}
if (self.push(deserialized) && data !== null) {
var read_batch = {}; var read_batch = {};
read_batch[grpc.opType.RECV_MESSAGE] = true; read_batch[grpc.opType.RECV_MESSAGE] = true;
self.call.startBatch(read_batch, readCallback); self.call.startBatch(read_batch, readCallback);
@ -354,7 +362,13 @@ function handleUnary(call, handler, metadata) {
handleError(call, err); handleError(call, err);
return; return;
} }
emitter.request = handler.deserialize(result.read); try {
emitter.request = handler.deserialize(result.read);
} catch (e) {
e.code = grpc.status.INVALID_ARGUMENT;
handleError(call, e);
return;
}
if (emitter.cancelled) { if (emitter.cancelled) {
return; return;
} }
@ -388,7 +402,13 @@ function handleServerStreaming(call, handler, metadata) {
stream.emit('error', err); stream.emit('error', err);
return; return;
} }
stream.request = handler.deserialize(result.read); try {
stream.request = handler.deserialize(result.read);
} catch (e) {
e.code = grpc.status.INVALID_ARGUMENT;
stream.emit('error', e);
return;
}
handler.func(stream); handler.func(stream);
}); });
} }
@ -401,6 +421,9 @@ function handleServerStreaming(call, handler, metadata) {
*/ */
function handleClientStreaming(call, handler, metadata) { function handleClientStreaming(call, handler, metadata) {
var stream = new ServerReadableStream(call, handler.deserialize); var stream = new ServerReadableStream(call, handler.deserialize);
stream.on('error', function(error) {
handleError(call, error);
});
waitForCancel(call, stream); waitForCancel(call, stream);
var metadata_batch = {}; var metadata_batch = {};
metadata_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata; metadata_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata;

@ -286,20 +286,24 @@ describe('end-to-end', function() {
assert.ifError(err); assert.ifError(err);
assert(response['send metadata']); assert(response['send metadata']);
assert.strictEqual(response.read.toString(), requests[0]); assert.strictEqual(response.read.toString(), requests[0]);
var end_batch = {}; var snd_batch = {};
end_batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true; snd_batch[grpc.opType.RECV_MESSAGE] = true;
end_batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { server_call.startBatch(snd_batch, function(err, response) {
'metadata': {},
'code': grpc.status.OK,
'details': status_text
};
end_batch[grpc.opType.RECV_MESSAGE] = true;
server_call.startBatch(end_batch, function(err, response) {
assert.ifError(err); assert.ifError(err);
assert(response['send status']);
assert(!response.cancelled);
assert.strictEqual(response.read.toString(), requests[1]); assert.strictEqual(response.read.toString(), requests[1]);
done(); var end_batch = {};
end_batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true;
end_batch[grpc.opType.SEND_STATUS_FROM_SERVER] = {
'metadata': {},
'code': grpc.status.OK,
'details': status_text
};
server_call.startBatch(end_batch, function(err, response) {
assert.ifError(err);
assert(response['send status']);
assert(!response.cancelled);
done();
});
}); });
}); });
}); });

@ -47,6 +47,8 @@ var mathService = math_proto.lookup('math.Math');
var capitalize = require('underscore.string/capitalize'); var capitalize = require('underscore.string/capitalize');
var _ = require('underscore');
describe('File loader', function() { describe('File loader', function() {
it('Should load a proto file by default', function() { it('Should load a proto file by default', function() {
assert.doesNotThrow(function() { assert.doesNotThrow(function() {
@ -178,9 +180,10 @@ describe('Generic client and server', function() {
}); });
}); });
}); });
describe('Trailing metadata', function() { describe('Other conditions', function() {
var client; var client;
var server; var server;
var port;
before(function() { before(function() {
var test_proto = ProtoBuf.loadProtoFile(__dirname + '/test_service.proto'); var test_proto = ProtoBuf.loadProtoFile(__dirname + '/test_service.proto');
var test_service = test_proto.lookup('TestService'); var test_service = test_proto.lookup('TestService');
@ -246,7 +249,7 @@ describe('Trailing metadata', function() {
} }
} }
}); });
var port = server.bind('localhost:0'); port = server.bind('localhost:0');
var Client = surface_client.makeProtobufClientConstructor(test_service); var Client = surface_client.makeProtobufClientConstructor(test_service);
client = new Client('localhost:' + port); client = new Client('localhost:' + port);
server.listen(); server.listen();
@ -254,86 +257,167 @@ describe('Trailing metadata', function() {
after(function() { after(function() {
server.shutdown(); server.shutdown();
}); });
it('should be present when a unary call succeeds', function(done) { describe('Server recieving bad input', function() {
var call = client.unary({error: false}, function(err, data) { var misbehavingClient;
assert.ifError(err); var badArg = new Buffer([0xFF]);
before(function() {
var test_service_attrs = {
unary: {
path: '/TestService/Unary',
requestStream: false,
responseStream: false,
requestSerialize: _.identity,
responseDeserialize: _.identity
},
clientStream: {
path: '/TestService/ClientStream',
requestStream: true,
responseStream: false,
requestSerialize: _.identity,
responseDeserialize: _.identity
},
serverStream: {
path: '/TestService/ServerStream',
requestStream: false,
responseStream: true,
requestSerialize: _.identity,
responseDeserialize: _.identity
},
bidiStream: {
path: '/TestService/BidiStream',
requestStream: true,
responseStream: true,
requestSerialize: _.identity,
responseDeserialize: _.identity
}
};
var Client = surface_client.makeClientConstructor(test_service_attrs,
'TestService');
misbehavingClient = new Client('localhost:' + port);
}); });
call.on('status', function(status) { it('should respond correctly to a unary call', function(done) {
assert.deepEqual(status.metadata.metadata, ['yes']); misbehavingClient.unary(badArg, function(err, data) {
done(); assert(err);
assert.strictEqual(err.code, grpc.status.INVALID_ARGUMENT);
done();
});
}); });
}); it('should respond correctly to a client stream', function(done) {
it('should be present when a unary call fails', function(done) { var call = misbehavingClient.clientStream(function(err, data) {
var call = client.unary({error: true}, function(err, data) { assert(err);
assert(err); assert.strictEqual(err.code, grpc.status.INVALID_ARGUMENT);
done();
});
call.write(badArg);
// TODO(mlumish): Remove call.end()
call.end();
}); });
call.on('status', function(status) { it('should respond correctly to a server stream', function(done) {
assert.deepEqual(status.metadata.metadata, ['yes']); var call = misbehavingClient.serverStream(badArg);
done(); call.on('data', function(data) {
assert.fail(data, null, 'Unexpected data', '===');
});
call.on('error', function(err) {
assert.strictEqual(err.code, grpc.status.INVALID_ARGUMENT);
done();
});
});
it('should respond correctly to a bidi stream', function(done) {
var call = misbehavingClient.bidiStream();
call.on('data', function(data) {
assert.fail(data, null, 'Unexpected data', '===');
});
call.on('error', function(err) {
assert.strictEqual(err.code, grpc.status.INVALID_ARGUMENT);
done();
});
call.write(badArg);
// TODO(mlumish): Remove call.end()
call.end();
}); });
}); });
it('should be present when a client stream call succeeds', function(done) { describe('Trailing metadata', function() {
var call = client.clientStream(function(err, data) { it('should be present when a unary call succeeds', function(done) {
assert.ifError(err); var call = client.unary({error: false}, function(err, data) {
assert.ifError(err);
});
call.on('status', function(status) {
assert.deepEqual(status.metadata.metadata, ['yes']);
done();
});
}); });
call.write({error: false}); it('should be present when a unary call fails', function(done) {
call.write({error: false}); var call = client.unary({error: true}, function(err, data) {
call.end(); assert(err);
call.on('status', function(status) { });
assert.deepEqual(status.metadata.metadata, ['yes']); call.on('status', function(status) {
done(); assert.deepEqual(status.metadata.metadata, ['yes']);
done();
});
}); });
}); it('should be present when a client stream call succeeds', function(done) {
it('should be present when a client stream call fails', function(done) { var call = client.clientStream(function(err, data) {
var call = client.clientStream(function(err, data) { assert.ifError(err);
assert(err); });
call.write({error: false});
call.write({error: false});
call.end();
call.on('status', function(status) {
assert.deepEqual(status.metadata.metadata, ['yes']);
done();
});
}); });
call.write({error: false}); it('should be present when a client stream call fails', function(done) {
call.write({error: true}); var call = client.clientStream(function(err, data) {
call.end(); assert(err);
call.on('status', function(status) { });
assert.deepEqual(status.metadata.metadata, ['yes']); call.write({error: false});
done(); call.write({error: true});
call.end();
call.on('status', function(status) {
assert.deepEqual(status.metadata.metadata, ['yes']);
done();
});
}); });
}); it('should be present when a server stream call succeeds', function(done) {
it('should be present when a server stream call succeeds', function(done) { var call = client.serverStream({error: false});
var call = client.serverStream({error: false}); call.on('data', function(){});
call.on('data', function(){}); call.on('status', function(status) {
call.on('status', function(status) { assert.strictEqual(status.code, grpc.status.OK);
assert.strictEqual(status.code, grpc.status.OK); assert.deepEqual(status.metadata.metadata, ['yes']);
assert.deepEqual(status.metadata.metadata, ['yes']); done();
done(); });
}); });
}); it('should be present when a server stream call fails', function(done) {
it('should be present when a server stream call fails', function(done) { var call = client.serverStream({error: true});
var call = client.serverStream({error: true}); call.on('data', function(){});
call.on('data', function(){}); call.on('error', function(error) {
call.on('error', function(error) { assert.deepEqual(error.metadata.metadata, ['yes']);
assert.deepEqual(error.metadata.metadata, ['yes']); done();
done(); });
}); });
}); it('should be present when a bidi stream succeeds', function(done) {
it('should be present when a bidi stream succeeds', function(done) { var call = client.bidiStream();
var call = client.bidiStream(); call.write({error: false});
call.write({error: false}); call.write({error: false});
call.write({error: false}); call.end();
call.end(); call.on('data', function(){});
call.on('data', function(){}); call.on('status', function(status) {
call.on('status', function(status) { assert.strictEqual(status.code, grpc.status.OK);
assert.strictEqual(status.code, grpc.status.OK); assert.deepEqual(status.metadata.metadata, ['yes']);
assert.deepEqual(status.metadata.metadata, ['yes']); done();
done(); });
}); });
}); it('should be present when a bidi stream fails', function(done) {
it('should be present when a bidi stream fails', function(done) { var call = client.bidiStream();
var call = client.bidiStream(); call.write({error: false});
call.write({error: false}); call.write({error: true});
call.write({error: true}); call.end();
call.end(); call.on('data', function(){});
call.on('data', function(){}); call.on('error', function(error) {
call.on('error', function(error) { assert.deepEqual(error.metadata.metadata, ['yes']);
assert.deepEqual(error.metadata.metadata, ['yes']); done();
done(); });
}); });
}); });
}); });

@ -231,7 +231,7 @@
handler:resumingHandler]] errorHandler:errorHandler]; handler:resumingHandler]] errorHandler:errorHandler];
} }
- (void)didReceiveValue:(id)value { - (void)writeValue:(id)value {
// TODO(jcanizales): Throw/assert if value isn't NSData. // TODO(jcanizales): Throw/assert if value isn't NSData.
// Pause the input and only resume it when the C layer notifies us that writes // Pause the input and only resume it when the C layer notifies us that writes
@ -255,7 +255,7 @@
errorHandler:errorHandler]; errorHandler:errorHandler];
} }
- (void)didFinishWithError:(NSError *)errorOrNil { - (void)writesFinishedWithError:(NSError *)errorOrNil {
if (errorOrNil) { if (errorOrNil) {
[self cancel]; [self cancel];
} else { } else {
@ -306,7 +306,7 @@
- (void)startWithWriteable:(id<GRXWriteable>)writeable { - (void)startWithWriteable:(id<GRXWriteable>)writeable {
// The following produces a retain cycle self:_responseWriteable:self, which is only // The following produces a retain cycle self:_responseWriteable:self, which is only
// broken when didFinishWithError: is sent to the wrapped writeable. // broken when writesFinishedWithError: is sent to the wrapped writeable.
// Care is taken not to retain self strongly in any of the blocks used in // Care is taken not to retain self strongly in any of the blocks used in
// the implementation of GRPCCall, so that the life of the instance is // the implementation of GRPCCall, so that the life of the instance is
// determined by this retain cycle. // determined by this retain cycle.

@ -38,11 +38,11 @@
// This is a thread-safe wrapper over a GRXWriteable instance. It lets one // This is a thread-safe wrapper over a GRXWriteable instance. It lets one
// enqueue calls to a GRXWriteable instance for the main thread, guaranteeing // enqueue calls to a GRXWriteable instance for the main thread, guaranteeing
// that didFinishWithError: is the last message sent to it (no matter what // that writesFinishedWithError: is the last message sent to it (no matter what
// messages are sent to the wrapper, in what order, nor from which thread). It // messages are sent to the wrapper, in what order, nor from which thread). It
// also guarantees that, if cancelWithError: is called from the main thread // also guarantees that, if cancelWithError: is called from the main thread
// (e.g. by the app cancelling the writes), no further messages are sent to the // (e.g. by the app cancelling the writes), no further messages are sent to the
// writeable except didFinishWithError:. // writeable except writesFinishedWithError:.
// //
// TODO(jcanizales): Let the user specify another queue for the writeable // TODO(jcanizales): Let the user specify another queue for the writeable
// callbacks. // callbacks.
@ -51,23 +51,22 @@
// The GRXWriteable passed is the wrapped writeable. // The GRXWriteable passed is the wrapped writeable.
// Both the GRXWriter instance and the GRXWriteable instance are retained until // Both the GRXWriter instance and the GRXWriteable instance are retained until
// didFinishWithError: is sent to the writeable, and released after that. // writesFinishedWithError: is sent to the writeable, and released after that.
// This is used to create a retain cycle that keeps both objects alive until the // This is used to create a retain cycle that keeps both objects alive until the
// writing is explicitly finished. // writing is explicitly finished.
- (instancetype)initWithWriteable:(id<GRXWriteable>)writeable writer:(id<GRXWriter>)writer - (instancetype)initWithWriteable:(id<GRXWriteable>)writeable writer:(id<GRXWriter>)writer
NS_DESIGNATED_INITIALIZER; NS_DESIGNATED_INITIALIZER;
// Enqueues didReceiveValue: to be sent to the writeable in the main thread. // Enqueues writeValue: to be sent to the writeable in the main thread.
// The passed handler is invoked from the main thread after didReceiveValue: // The passed handler is invoked from the main thread after writeValue: returns.
// returns.
- (void)enqueueMessage:(NSData *)message completionHandler:(void (^)())handler; - (void)enqueueMessage:(NSData *)message completionHandler:(void (^)())handler;
// Enqueues didFinishWithError:nil to be sent to the writeable in the main // Enqueues writesFinishedWithError:nil to be sent to the writeable in the main
// thread. After that message is sent to the writeable, all other methods of // thread. After that message is sent to the writeable, all other methods of
// this object are effectively noops. // this object are effectively noops.
- (void)enqueueSuccessfulCompletion; - (void)enqueueSuccessfulCompletion;
// If the writeable has not yet received a didFinishWithError: message, this // If the writeable has not yet received a writesFinishedWithError: message, this
// will enqueue one to be sent to it in the main thread, and cancel all other // will enqueue one to be sent to it in the main thread, and cancel all other
// pending messages to the writeable enqueued by this object (both past and // pending messages to the writeable enqueued by this object (both past and
// future). // future).
@ -75,7 +74,7 @@
- (void)cancelWithError:(NSError *)error; - (void)cancelWithError:(NSError *)error;
// Cancels all pending messages to the writeable enqueued by this object (both // Cancels all pending messages to the writeable enqueued by this object (both
// past and future). Because the writeable won't receive didFinishWithError:, // past and future). Because the writeable won't receive writesFinishedWithError:,
// this also releases the writeable and the writer. // this also releases the writeable and the writer.
- (void)cancelSilently; - (void)cancelSilently;
@end @end

@ -43,7 +43,7 @@
@implementation GRPCDelegateWrapper { @implementation GRPCDelegateWrapper {
dispatch_queue_t _writeableQueue; dispatch_queue_t _writeableQueue;
// This ensures that didFinishWithError: is only sent once to the writeable. // This ensures that writesFinishedWithError: is only sent once to the writeable.
dispatch_once_t _alreadyFinished; dispatch_once_t _alreadyFinished;
} }
@ -69,7 +69,7 @@
// the race. // the race.
id<GRXWriteable> writeable = self.writeable; id<GRXWriteable> writeable = self.writeable;
if (writeable) { if (writeable) {
[writeable didReceiveValue:message]; [writeable writeValue:message];
handler(); handler();
} }
}); });
@ -80,7 +80,7 @@
dispatch_once(&_alreadyFinished, ^{ dispatch_once(&_alreadyFinished, ^{
// Cancellation is now impossible. None of the other three blocks can run // Cancellation is now impossible. None of the other three blocks can run
// concurrently with this one. // concurrently with this one.
[self.writeable didFinishWithError:nil]; [self.writeable writesFinishedWithError:nil];
// Break the retain cycle with writer, and skip any possible message to the // Break the retain cycle with writer, and skip any possible message to the
// wrapped writeable enqueued after this one. // wrapped writeable enqueued after this one.
self.writeable = nil; self.writeable = nil;
@ -100,7 +100,7 @@
self.writeable = nil; self.writeable = nil;
dispatch_async(_writeableQueue, ^{ dispatch_async(_writeableQueue, ^{
[writeable didFinishWithError:error]; [writeable writesFinishedWithError:error];
// Break the retain cycle with writer. // Break the retain cycle with writer.
self.writer = nil; self.writer = nil;
}); });

@ -71,9 +71,9 @@
if ((self = [super initWithHost:host method:method requestsWriter:bytesWriter])) { if ((self = [super initWithHost:host method:method requestsWriter:bytesWriter])) {
// A writeable that parses the proto messages received. // A writeable that parses the proto messages received.
_responseWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) { _responseWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
[responsesWriteable didReceiveValue:[responseClass parseFromData:value]]; [responsesWriteable writeValue:[responseClass parseFromData:value]];
} completionHandler:^(NSError *errorOrNil) { } completionHandler:^(NSError *errorOrNil) {
[responsesWriteable didFinishWithError:errorOrNil]; [responsesWriteable writesFinishedWithError:errorOrNil];
}]; }];
} }
return self; return self;

@ -1,3 +1,47 @@
gRPC implementation for Objective-C on iOS # gRPC for Objective-C
This is a work in progress. ## How to generate a client library from a Protocol Buffers definition
First install v3 of the Protocol Buffers compiler (_protoc_), by cloning [its Git repository](https://github.com/google/protobuf) and following these [installation instructions](https://github.com/google/protobuf#c-installation---unix) (the ones titled C++; don't miss the note for Mac users).
Then clone this repository and execute the following commands from the root directory where it was cloned.
Compile the gRPC plugins for _protoc_:
```sh
make plugins
```
Create a symbolic link to the compiled plugin binary somewhere in your `$PATH`:
```sh
ln -s `pwd`/bins/opt/grpc_objective_c_plugin /usr/local/bin/protoc-gen-objcgrpc
```
(Notice that the name of the created link must begin with "protoc-gen-" for _protoc_ to recognize it as a plugin).
If you don't want to create the symbolic link, you can alternatively copy the binary (with the appropriate name). Or you might prefer instead to specify the plugin's path as a flag when invoking _protoc_, in which case no system modification nor renaming is necessary.
Finally, run _protoc_ with the following flags to generate the client library for your `.proto` files:
```sh
protoc --objc_out=. --objcgrpc_out=. *.proto
```
This will generate a pair of `.pbobjc.h`/`.pbobjc.m` files for each `.proto` file, with the messages and enums defined in them. And a pair of `.pbrpc.h`/`.pbrpc.m` files for each `.proto` file with services defined. The latter contains the code to make remote calls to the specified API.
## How to integrate a generated gRPC library in your project
### If you use Cocoapods
This is the recommended approach.
You need to create a Podspec file for the generated library. This is simply a matter of copying an example like [this one](https://github.com/grpc/grpc/blob/master/src/objective-c/examples/Sample/RemoteTestClient/RemoteTest.podspec) to the directory where the source files were generated. Update the name and other metadata of the Podspec as suitable.
Once your library has a Podspec, refer to it from your Podfile using `:path` as described [here](https://guides.cocoapods.org/using/the-podfile.html#using-the-files-from-a-folder-local-to-the-machine).
### If you don't use Cocoapods
You need to compile the generated `.pbpbjc.*` files (the enums and messages) without ARC support, and the generated `.pbrpc.*` files (the services) with ARC support. The generated code depends on v0.3+ of the Objective-C gRPC runtime library and v3.0+ of the Objective-C Protobuf runtime library.
These libraries need to be integrated into your project as described in their respective Podspec files:
* [Podspec](https://github.com/grpc/grpc/blob/master/gRPC.podspec) for the Objective-C gRPC runtime library. This can be tedious to configure manually.
* [Podspec](https://github.com/jcanizales/protobuf/blob/add-podspec/Protobuf.podspec) for the Objective-C Protobuf runtime library.

@ -0,0 +1,59 @@
/*
*
* 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.
*
*/
#import <Foundation/Foundation.h>
#import "GRXWriteable.h"
#import "GRXWriter.h"
// A buffered pipe is a Writeable that also acts as a Writer (to whichever other writeable is passed
// to -startWithWriteable:).
// Once it is started, whatever values are written into it (via -writeValue:) will be propagated
// immediately, unless flow control prevents it.
// If it is throttled and keeps receiving values, as well as if it receives values before being
// started, it will buffer them and propagate them in order as soon as its state becomes
// GRXWriterStateStarted.
// If it receives an error (via -writesFinishedWithError:), it will drop any buffered values and
// propagate the error immediately.
//
// Beware that a pipe of this type can't prevent receiving more values when it is paused (for
// example if used to write data to a congested network connection). Because in such situations the
// pipe will keep buffering all data written to it, your application could run out of memory and
// crash. If you want to react to flow control signals to prevent that, instead of using this class
// you can implement an object that conforms to GRXWriter.
@interface GRXBufferedPipe : NSObject<GRXWriteable, GRXWriter>
// Convenience constructor.
+ (instancetype)pipe;
@end

@ -0,0 +1,146 @@
/*
*
* 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.
*
*/
#import "GRXBufferedPipe.h"
@implementation GRXBufferedPipe {
id<GRXWriteable> _writeable;
NSMutableArray *_queue;
BOOL _inputIsFinished;
NSError *_errorOrNil;
}
@synthesize state = _state;
+ (instancetype)pipe {
return [[self alloc] init];
}
- (instancetype)init {
if (self = [super init]) {
_queue = [NSMutableArray array];
_state = GRXWriterStateNotStarted;
}
return self;
}
- (id)popValue {
id value = _queue[0];
[_queue removeObjectAtIndex:0];
return value;
}
- (void)writeBufferUntilPausedOrStopped {
while (_state == GRXWriterStateStarted && _queue.count > 0) {
[_writeable writeValue:[self popValue]];
}
if (_inputIsFinished && _queue.count == 0) {
// Our writer finished normally while we were paused or not-started-yet.
[self finishWithError:_errorOrNil];
}
}
#pragma mark GRXWriteable implementation
// Returns whether events can be simply propagated to the other end of the pipe.
- (BOOL)shouldFastForward {
return _state == GRXWriterStateStarted && _queue.count == 0;
}
- (void)writeValue:(id)value {
if (self.shouldFastForward) {
// Skip the queue.
[_writeable writeValue:value];
} else {
// Even if we're paused and with enqueued values, we can't excert back-pressure to our writer.
// So just buffer the new value.
// We need a copy, so that it doesn't mutate before it's written at the other end of the pipe.
if ([value respondsToSelector:@selector(copy)]) {
value = [value copy];
}
[_queue addObject:value];
}
}
- (void)writesFinishedWithError:(NSError *)errorOrNil {
_inputIsFinished = YES;
_errorOrNil = errorOrNil;
if (errorOrNil || self.shouldFastForward) {
// No need to write pending values.
[self finishWithError:_errorOrNil];
}
}
#pragma mark GRXWriter implementation
- (void)setState:(GRXWriterState)newState {
// Manual transitions are only allowed from the started or paused states.
if (_state == GRXWriterStateNotStarted || _state == GRXWriterStateFinished) {
return;
}
switch (newState) {
case GRXWriterStateFinished:
_state = newState;
_queue = nil;
// Per GRXWriter's contract, setting the state to Finished manually means one doesn't wish the
// writeable to be messaged anymore.
_writeable = nil;
return;
case GRXWriterStatePaused:
_state = newState;
return;
case GRXWriterStateStarted:
if (_state == GRXWriterStatePaused) {
_state = newState;
[self writeBufferUntilPausedOrStopped];
}
return;
case GRXWriterStateNotStarted:
return;
}
}
- (void)startWithWriteable:(id<GRXWriteable>)writeable {
_state = GRXWriterStateStarted;
_writeable = writeable;
[self writeBufferUntilPausedOrStopped];
}
- (void)finishWithError:(NSError *)errorOrNil {
id<GRXWriteable> writeable = _writeable;
self.state = GRXWriterStateFinished;
[writeable writesFinishedWithError:errorOrNil];
}
@end

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

Loading…
Cancel
Save