Merge branch 'master' into poisson

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

@ -32,7 +32,7 @@ script:
- 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 16 -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 = [

1310
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,8 @@
"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"
] ]
}, },
{ {
@ -1814,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"
@ -1832,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"
@ -2226,7 +2224,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"
@ -2244,7 +2241,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());

@ -67,7 +67,8 @@ Pod::Spec.new do |s|
s.subspec 'GRPCClient' do |gs| s.subspec 'GRPCClient' do |gs|
gs.summary = 'Objective-C wrapper around the core gRPC library.' gs.summary = 'Objective-C wrapper around the core gRPC library.'
gs.authors = { 'Jorge Canizales' => 'jcanizales@google.com' } gs.authors = { 'Jorge Canizales' => 'jcanizales@google.com',
'Michael Lumish' => 'mlumish@google.com' }
gs.source_files = 'src/objective-c/GRPCClient/*.{h,m}', gs.source_files = 'src/objective-c/GRPCClient/*.{h,m}',
'src/objective-c/GRPCClient/private/*.{h,m}' 'src/objective-c/GRPCClient/private/*.{h,m}'
@ -90,5 +91,6 @@ Pod::Spec.new do |s|
ps.dependency 'gRPC/GRPCClient' ps.dependency 'gRPC/GRPCClient'
ps.dependency 'gRPC/RxLibrary' ps.dependency 'gRPC/RxLibrary'
ps.dependency 'Protobuf', '~> 3.0'
end end
end end

@ -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_;

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

@ -37,7 +37,7 @@
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, char **argv);
/* if subprocess has not been joined, kill it */ /* if subprocess has not been joined, kill it */

@ -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;
} }
string file_name = grpc_generator::FileNameInUpperCamel(file);
string prefix = file->options().objc_class_prefix();
{
// Generate .pbrpc.h
string imports = string("#import \"") + file_name + ".pbobjc.h\"\n"
"#import <gRPC/ProtoService.h>\n";
// TODO(jcanizales): Instead forward-declare the input and output types
// and import the files in the .pbrpc.m
string proto_imports;
for (int i = 0; i < file->dependency_count(); i++) {
string header = grpc_objective_c_generator::MessageHeaderName(
file->dependency(i));
proto_imports += string("#import \"") + header + "\"\n";
}
string declarations;
for (int i = 0; i < file->service_count(); i++) {
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++) { for (int i = 0; i < file->service_count(); i++) {
const grpc::protobuf::ServiceDescriptor *service = file->service(i); const grpc::protobuf::ServiceDescriptor *service = file->service(i);
grpc::string file_name = grpc_objective_c_generator::StubFileName( definitions += grpc_objective_c_generator::GetSource(service, prefix);
service->name()); }
// Generate .pb.h Write(context, file_name + ".pbrpc.m", imports + '\n' + definitions);
grpc::string header_code = grpc_objective_c_generator::GetHeader(
service, grpc_objective_c_generator::MessageHeaderName(file));
std::unique_ptr<grpc::protobuf::io::ZeroCopyOutputStream> header_output(
context->Open(file_name + ".pb.h"));
grpc::protobuf::io::CodedOutputStream header_coded_out(
header_output.get());
header_coded_out.WriteRaw(header_code.data(), header_code.size());
// Generate .pb.m
grpc::string source_code = grpc_objective_c_generator::GetSource(service);
std::unique_ptr<grpc::protobuf::io::ZeroCopyOutputStream> source_output(
context->Open(file_name + ".pb.m"));
grpc::protobuf::io::CodedOutputStream source_coded_out(
source_output.get());
source_coded_out.WriteRaw(source_code.data(), source_code.size());
} }
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());
} }

@ -34,7 +34,6 @@
#include "src/core/channel/http_server_filter.h" #include "src/core/channel/http_server_filter.h"
#include <string.h> #include <string.h>
#include <grpc/grpc_http.h>
#include <grpc/support/alloc.h> #include <grpc/support/alloc.h>
#include <grpc/support/log.h> #include <grpc/support/log.h>

@ -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.

@ -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);
void grpc_timer_add_important_mark(int tag, const char *tagstr, 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); int line);
void grpc_timer_begin(int tag, void *id, const char *file, int line);
void grpc_timer_end(int tag, 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. */
@ -105,23 +108,25 @@ enum grpc_profiling_tags {
/* 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,7 +55,7 @@ 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, char **argv) {
gpr_subprocess *r; gpr_subprocess *r;

@ -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) {
@ -597,6 +597,7 @@ static void call_on_done_send(void *pc, int 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->send_ops.nops = 0;
call->last_send_contains = 0; call->last_send_contains = 0;
call->sending = 0; call->sending = 0;
unlock(call); unlock(call);

@ -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;

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

@ -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,6 +1230,7 @@ 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;
if (error_code != GRPC_CHTTP2_NO_ERROR) {
schedule_nuke_sopb(t, &s->parser.incoming_sopb); schedule_nuke_sopb(t, &s->parser.incoming_sopb);
if (s->outgoing_sopb) { if (s->outgoing_sopb) {
schedule_nuke_sopb(t, s->outgoing_sopb); schedule_nuke_sopb(t, s->outgoing_sopb);
@ -1221,6 +1238,7 @@ static void cancel_stream_inner(transport *t, stream *s, gpr_uint32 id,
stream_list_remove(t, s, WRITABLE); stream_list_remove(t, s, WRITABLE);
schedule_cb(t, s->send_done_closure, 0); schedule_cb(t, s->send_done_closure, 0);
} }
}
if (s->cancelled) { if (s->cancelled) {
send_rst = 0; send_rst = 0;
} else if (!s->read_closed || s->write_state != WRITE_STATE_SENT_CLOSE || } else if (!s->read_closed || s->write_state != WRITE_STATE_SENT_CLOSE ||
@ -1228,6 +1246,8 @@ 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);
if (error_code != GRPC_CHTTP2_NO_ERROR) {
/* synthesize a status if we don't believe we'll get one */
gpr_ltoa(local_status, buffer); gpr_ltoa(local_status, buffer);
add_incoming_metadata( add_incoming_metadata(
t, s, t, s,
@ -1251,9 +1271,10 @@ static void cancel_stream_inner(transport *t, stream *s, gpr_uint32 id,
grpc_mdstr_ref(optional_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) {
gpr_slice_buffer_add(&t->qbuf, gpr_slice_buffer_add(&t->qbuf,
@ -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;

@ -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)

@ -37,6 +37,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,6 +16,7 @@
<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="Ix-Async" version="1.2.3" />
<dependency id="grpc.native.csharp_ext" version="0.8.0.0" /> <dependency id="grpc.native.csharp_ext" version="0.8.0.0" />
</dependencies> </dependencies>
</metadata> </metadata>

@ -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( var numbers = new List<long> { 10, 20, 30 }.ConvertAll(
n => Num.CreateBuilder().SetNum_(n).Build()); 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); await call.RequestStream.WriteAll(divArgsList);
var result = await call.ResponseStream.ToList(); 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,19 +51,14 @@ 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(); List<Num> result = await call.ResponseStream.ToList();
Console.WriteLine("Fib Result: " + string.Join("|", result)); Console.WriteLine("Fib Result: " + string.Join("|", result));
} }
}
public static async Task SumExample(Math.IMathClient stub) public static async Task SumExample(Math.IMathClient stub)
{ {
@ -74,10 +69,12 @@ 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); await call.RequestStream.WriteAll(numbers);
Console.WriteLine("Sum Result: " + await call.Result); Console.WriteLine("Sum Result: " + await call.Result);
} }
}
public static async Task DivManyExample(Math.IMathClient stub) public static async Task DivManyExample(Math.IMathClient stub)
{ {
@ -87,10 +84,12 @@ 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); await call.RequestStream.WriteAll(divArgsList);
Console.WriteLine("DivMany Result: " + string.Join("|", await call.ResponseStream.ToList())); 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;
using (var sumCall = stub.Sum())
{
await sumCall.RequestStream.WriteAll(numbers); await sumCall.RequestStream.WriteAll(numbers);
Num sum = await sumCall.Result; 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(); var responseList = await call.ResponseStream.ToList();
foreach (var res in responseList) foreach (var res in responseList)
{ {
Assert.AreEqual(PayloadType.COMPRESSABLE, res.Payload.Type); 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()
await call.RequestStream.Write(StreamingOutputCallRequest.CreateBuilder()
.SetResponseType(PayloadType.COMPRESSABLE) .SetResponseType(PayloadType.COMPRESSABLE)
.AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(31415)) .AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(31415))
.SetPayload(CreateZerosPayload(27182)).Build()); .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);
await call.RequestStream.Write(StreamingOutputCallRequest.CreateBuilder() await call.RequestStream.WriteAsync(StreamingOutputCallRequest.CreateBuilder()
.SetResponseType(PayloadType.COMPRESSABLE) .SetResponseType(PayloadType.COMPRESSABLE)
.AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(9)) .AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(9))
.SetPayload(CreateZerosPayload(8)).Build()); .SetPayload(CreateZerosPayload(8)).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(9, response.Payload.Body.Length); Assert.AreEqual(9, call.ResponseStream.Current.Payload.Body.Length);
await call.RequestStream.Write(StreamingOutputCallRequest.CreateBuilder() await call.RequestStream.WriteAsync(StreamingOutputCallRequest.CreateBuilder()
.SetResponseType(PayloadType.COMPRESSABLE) .SetResponseType(PayloadType.COMPRESSABLE)
.AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(2653)) .AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(2653))
.SetPayload(CreateZerosPayload(1828)).Build()); .SetPayload(CreateZerosPayload(1828)).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(2653, response.Payload.Body.Length); Assert.AreEqual(2653, call.ResponseStream.Current.Payload.Body.Length);
await call.RequestStream.Write(StreamingOutputCallRequest.CreateBuilder() await call.RequestStream.WriteAsync(StreamingOutputCallRequest.CreateBuilder()
.SetResponseType(PayloadType.COMPRESSABLE) .SetResponseType(PayloadType.COMPRESSABLE)
.AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(58979)) .AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(58979))
.SetPayload(CreateZerosPayload(45904)).Build()); .SetPayload(CreateZerosPayload(45904)).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(58979, response.Payload.Body.Length); Assert.AreEqual(58979, call.ResponseStream.Current.Payload.Body.Length);
await call.RequestStream.Close();
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(); var responseList = await call.ResponseStream.ToList();
Assert.AreEqual(0, responseList.Count); Assert.AreEqual(0, responseList.Count);
}
Console.WriteLine("Passed!"); Console.WriteLine("Passed!");
}).Wait(); }).Wait();
} }
@ -365,7 +366,8 @@ 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. // TODO(jtattermusch): we need this to ensure call has been initiated once we cancel it.
await Task.Delay(1000); await Task.Delay(1000);
cts.Cancel(); cts.Cancel();
@ -379,6 +381,7 @@ namespace Grpc.IntegrationTesting
{ {
Assert.AreEqual(StatusCode.Cancelled, e.Status.StatusCode); Assert.AreEqual(StatusCode.Cancelled, e.Status.StatusCode);
} }
}
Console.WriteLine("Passed!"); Console.WriteLine("Passed!");
}).Wait(); }).Wait();
} }
@ -390,30 +393,29 @@ 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()
await call.RequestStream.Write(StreamingOutputCallRequest.CreateBuilder()
.SetResponseType(PayloadType.COMPRESSABLE) .SetResponseType(PayloadType.COMPRESSABLE)
.AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(31415)) .AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(31415))
.SetPayload(CreateZerosPayload(27182)).Build()); .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" />

@ -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"
]
}

@ -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",

@ -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;
} }
try {
emitter.request = handler.deserialize(result.read); 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;
} }
try {
stream.request = handler.deserialize(result.read); 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,6 +286,11 @@ 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 snd_batch = {};
snd_batch[grpc.opType.RECV_MESSAGE] = true;
server_call.startBatch(snd_batch, function(err, response) {
assert.ifError(err);
assert.strictEqual(response.read.toString(), requests[1]);
var end_batch = {}; var end_batch = {};
end_batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true; end_batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true;
end_batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { end_batch[grpc.opType.SEND_STATUS_FROM_SERVER] = {
@ -293,15 +298,14 @@ describe('end-to-end', function() {
'code': grpc.status.OK, 'code': grpc.status.OK,
'details': status_text 'details': status_text
}; };
end_batch[grpc.opType.RECV_MESSAGE] = true;
server_call.startBatch(end_batch, function(err, response) { server_call.startBatch(end_batch, function(err, response) {
assert.ifError(err); assert.ifError(err);
assert(response['send status']); assert(response['send status']);
assert(!response.cancelled); assert(!response.cancelled);
assert.strictEqual(response.read.toString(), requests[1]);
done(); 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,6 +257,86 @@ describe('Trailing metadata', function() {
after(function() { after(function() {
server.shutdown(); server.shutdown();
}); });
describe('Server recieving bad input', function() {
var misbehavingClient;
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);
});
it('should respond correctly to a unary call', function(done) {
misbehavingClient.unary(badArg, function(err, data) {
assert(err);
assert.strictEqual(err.code, grpc.status.INVALID_ARGUMENT);
done();
});
});
it('should respond correctly to a client stream', function(done) {
var call = misbehavingClient.clientStream(function(err, data) {
assert(err);
assert.strictEqual(err.code, grpc.status.INVALID_ARGUMENT);
done();
});
call.write(badArg);
// TODO(mlumish): Remove call.end()
call.end();
});
it('should respond correctly to a server stream', function(done) {
var call = misbehavingClient.serverStream(badArg);
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();
});
});
describe('Trailing metadata', function() {
it('should be present when a unary call succeeds', function(done) { it('should be present when a unary call succeeds', function(done) {
var call = client.unary({error: false}, function(err, data) { var call = client.unary({error: false}, function(err, data) {
assert.ifError(err); assert.ifError(err);
@ -336,6 +419,7 @@ describe('Trailing metadata', function() {
done(); done();
}); });
}); });
});
}); });
describe('Cancelling surface client', function() { describe('Cancelling surface client', function() {
var client; var client;

@ -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;
}); });

@ -36,7 +36,7 @@
#import <gRPC/GRXWriteable.h> #import <gRPC/GRXWriteable.h>
#import <gRPC/GRXWriter.h> #import <gRPC/GRXWriter.h>
#import <gRPC/GRXWriter+Transformations.h> #import <gRPC/GRXWriter+Transformations.h>
#import <ProtocolBuffers/ProtocolBuffers.h> #import <Protobuf/GPBProtocolBuffers.h>
@implementation ProtoRPC { @implementation ProtoRPC {
id<GRXWriteable> _responseWriteable; id<GRXWriteable> _responseWriteable;
@ -65,15 +65,15 @@
} }
// A writer that serializes the proto messages to send. // A writer that serializes the proto messages to send.
id<GRXWriter> bytesWriter = id<GRXWriter> bytesWriter =
[[[GRXWriter alloc] initWithWriter:requestsWriter] map:^id(PBGeneratedMessage *proto) { [[[GRXWriter alloc] initWithWriter:requestsWriter] map:^id(GPBMessage *proto) {
return [proto data]; return [proto data];
}]; }];
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=. --objcrpc_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

@ -109,7 +109,7 @@
- (void)writeUntilPausedOrStopped { - (void)writeUntilPausedOrStopped {
id value; id value;
while (value = [_enumerator nextObject]) { while (value = [_enumerator nextObject]) {
[_writeable didReceiveValue:value]; [_writeable writeValue:value];
// If the writeable has a reference to us, it might change our state to paused or finished. // If the writeable has a reference to us, it might change our state to paused or finished.
if (_state == GRXWriterStatePaused || _state == GRXWriterStateFinished) { if (_state == GRXWriterStatePaused || _state == GRXWriterStateFinished) {
return; return;
@ -130,7 +130,7 @@
_errorOrNil = nil; _errorOrNil = nil;
id<GRXWriteable> writeable = _writeable; id<GRXWriteable> writeable = _writeable;
_writeable = nil; _writeable = nil;
[writeable didFinishWithError:errorOrNil]; [writeable writesFinishedWithError:errorOrNil];
} }
- (void)setState:(GRXWriterState)newState { - (void)setState:(GRXWriterState)newState {

@ -38,14 +38,12 @@
@protocol GRXWriteable <NSObject> @protocol GRXWriteable <NSObject>
// Push the next value of the sequence to the receiving object. // Push the next value of the sequence to the receiving object.
// TODO(jcanizales): Name it enumerator:(id<GRXEnumerator>) didProduceValue:(id)? - (void)writeValue:(id)value;
- (void)didReceiveValue:(id)value;
// Signal that the sequence is completed, or that an error ocurred. After this // Signal that the sequence is completed, or that an error ocurred. After this
// message is sent to the instance, neither it nor didReceiveValue: may be // message is sent to the instance, neither it nor writeValue: may be
// called again. // called again.
// TODO(jcanizales): enumerator:(id<GRXEnumerator>) didFinishWithError:(NSError*)? - (void)writesFinishedWithError:(NSError *)errorOrNil;
- (void)didFinishWithError:(NSError *)errorOrNil;
@end @end
typedef void (^GRXValueHandler)(id value); typedef void (^GRXValueHandler)(id value);

@ -76,13 +76,13 @@
return self; return self;
} }
- (void)didReceiveValue:(id)value { - (void)writeValue:(id)value {
if (_valueHandler) { if (_valueHandler) {
_valueHandler(value); _valueHandler(value);
} }
} }
- (void)didFinishWithError:(NSError *)errorOrNil { - (void)writesFinishedWithError:(NSError *)errorOrNil {
if (_completionHandler) { if (_completionHandler) {
_completionHandler(errorOrNil); _completionHandler(errorOrNil);
} }

@ -50,7 +50,7 @@ typedef NS_ENUM(NSInteger, GRXWriterState) {
// The writer is temporarily paused, and won't send any more values to the // The writer is temporarily paused, and won't send any more values to the
// writeable unless its state is set back to Started. The writer might still // writeable unless its state is set back to Started. The writer might still
// transition to the Finished state at any moment, and is allowed to send // transition to the Finished state at any moment, and is allowed to send
// didFinishWithError: to its writeable. // writesFinishedWithError: to its writeable.
// //
// Not all implementations of writer have to support pausing, and thus // Not all implementations of writer have to support pausing, and thus
// trying to set an writer's state to this value might have no effect. // trying to set an writer's state to this value might have no effect.
@ -59,7 +59,7 @@ typedef NS_ENUM(NSInteger, GRXWriterState) {
// The writer has released its writeable and won't interact with it anymore. // The writer has released its writeable and won't interact with it anymore.
// //
// One seldomly wants to set an writer's state to this value, as its // One seldomly wants to set an writer's state to this value, as its
// writeable isn't notified with a didFinishWithError: message. Instead, sending // writeable isn't notified with a writesFinishedWithError: message. Instead, sending
// finishWithError: to the writer will make it notify the writeable and then // finishWithError: to the writer will make it notify the writeable and then
// transition to this state. // transition to this state.
GRXWriterStateFinished GRXWriterStateFinished
@ -105,7 +105,7 @@ typedef NS_ENUM(NSInteger, GRXWriterState) {
// This method might only be called on writers in the NotStarted state. // This method might only be called on writers in the NotStarted state.
- (void)startWithWriteable:(id<GRXWriteable>)writeable; - (void)startWithWriteable:(id<GRXWriteable>)writeable;
// Send didFinishWithError:errorOrNil immediately to the writeable, and don't send // Send writesFinishedWithError:errorOrNil immediately to the writeable, and don't send
// any more messages to it. // any more messages to it.
// //
// This method might only be called on writers in the Started or Paused // This method might only be called on writers in the Started or Paused

@ -62,7 +62,7 @@
- (void)finishOutputWithError:(NSError *)errorOrNil { - (void)finishOutputWithError:(NSError *)errorOrNil {
id<GRXWriteable> writeable = _writeable; id<GRXWriteable> writeable = _writeable;
_writeable = nil; _writeable = nil;
[writeable didFinishWithError:errorOrNil]; [writeable writesFinishedWithError:errorOrNil];
} }
// This is used to stop the input writer. It nillifies our reference to it // This is used to stop the input writer. It nillifies our reference to it
@ -75,11 +75,11 @@
#pragma mark GRXWriteable implementation #pragma mark GRXWriteable implementation
- (void)didReceiveValue:(id)value { - (void)writeValue:(id)value {
[_writeable didReceiveValue:value]; [_writeable writeValue:value];
} }
- (void)didFinishWithError:(NSError *)errorOrNil { - (void)writesFinishedWithError:(NSError *)errorOrNil {
_writer = nil; _writer = nil;
[self finishOutputWithError:errorOrNil]; [self finishOutputWithError:errorOrNil];
} }

@ -57,7 +57,7 @@ static id (^kIdentity)(id value) = ^id(id value) {
} }
// Override // Override
- (void)didReceiveValue:(id)value { - (void)writeValue:(id)value {
[super didReceiveValue:_map(value)]; [super writeValue:_map(value)];
} }
@end @end

@ -2,6 +2,7 @@ source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0' platform :ios, '8.0'
pod 'gRPC', :path => "../../../.." pod 'gRPC', :path => "../../../.."
pod 'Protobuf', :git => 'https://github.com/jcanizales/protobuf.git', :branch => 'add-podspec'
pod 'Route_guide', :path => "RouteGuideClient" pod 'Route_guide', :path => "RouteGuideClient"
pod 'RemoteTest', :path => "RemoteTestClient" pod 'RemoteTest', :path => "RemoteTestClient"

@ -1,103 +0,0 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
#import <ProtocolBuffers/ProtocolBuffers.h>
// @@protoc_insertion_point(imports)
@class ObjectiveCFileOptions;
@class ObjectiveCFileOptionsBuilder;
@class PBDescriptorProto;
@class PBDescriptorProtoBuilder;
@class PBDescriptorProtoExtensionRange;
@class PBDescriptorProtoExtensionRangeBuilder;
@class PBEnumDescriptorProto;
@class PBEnumDescriptorProtoBuilder;
@class PBEnumOptions;
@class PBEnumOptionsBuilder;
@class PBEnumValueDescriptorProto;
@class PBEnumValueDescriptorProtoBuilder;
@class PBEnumValueOptions;
@class PBEnumValueOptionsBuilder;
@class PBFieldDescriptorProto;
@class PBFieldDescriptorProtoBuilder;
@class PBFieldOptions;
@class PBFieldOptionsBuilder;
@class PBFileDescriptorProto;
@class PBFileDescriptorProtoBuilder;
@class PBFileDescriptorSet;
@class PBFileDescriptorSetBuilder;
@class PBFileOptions;
@class PBFileOptionsBuilder;
@class PBMessageOptions;
@class PBMessageOptionsBuilder;
@class PBMethodDescriptorProto;
@class PBMethodDescriptorProtoBuilder;
@class PBMethodOptions;
@class PBMethodOptionsBuilder;
@class PBOneofDescriptorProto;
@class PBOneofDescriptorProtoBuilder;
@class PBServiceDescriptorProto;
@class PBServiceDescriptorProtoBuilder;
@class PBServiceOptions;
@class PBServiceOptionsBuilder;
@class PBSourceCodeInfo;
@class PBSourceCodeInfoBuilder;
@class PBSourceCodeInfoLocation;
@class PBSourceCodeInfoLocationBuilder;
@class PBUninterpretedOption;
@class PBUninterpretedOptionBuilder;
@class PBUninterpretedOptionNamePart;
@class PBUninterpretedOptionNamePartBuilder;
@class RMTEmpty;
@class RMTEmptyBuilder;
@interface RMTEmptyRoot : NSObject {
}
+ (PBExtensionRegistry*) extensionRegistry;
+ (void) registerAllExtensions:(PBMutableExtensionRegistry*) registry;
@end
@interface RMTEmpty : PBGeneratedMessage<GeneratedMessageProtocol> {
@private
}
+ (instancetype) defaultInstance;
- (instancetype) defaultInstance;
- (BOOL) isInitialized;
- (void) writeToCodedOutputStream:(PBCodedOutputStream*) output;
- (RMTEmptyBuilder*) builder;
+ (RMTEmptyBuilder*) builder;
+ (RMTEmptyBuilder*) builderWithPrototype:(RMTEmpty*) prototype;
- (RMTEmptyBuilder*) toBuilder;
+ (RMTEmpty*) parseFromData:(NSData*) data;
+ (RMTEmpty*) parseFromData:(NSData*) data extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
+ (RMTEmpty*) parseFromInputStream:(NSInputStream*) input;
+ (RMTEmpty*) parseFromInputStream:(NSInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
+ (RMTEmpty*) parseFromCodedInputStream:(PBCodedInputStream*) input;
+ (RMTEmpty*) parseFromCodedInputStream:(PBCodedInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
@end
@interface RMTEmptyBuilder : PBGeneratedMessageBuilder {
@private
RMTEmpty* resultEmpty;
}
- (RMTEmpty*) defaultInstance;
- (RMTEmptyBuilder*) clear;
- (RMTEmptyBuilder*) clone;
- (RMTEmpty*) build;
- (RMTEmpty*) buildPartial;
- (RMTEmptyBuilder*) mergeFrom:(RMTEmpty*) other;
- (RMTEmptyBuilder*) mergeFromCodedInputStream:(PBCodedInputStream*) input;
- (RMTEmptyBuilder*) mergeFromCodedInputStream:(PBCodedInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
@end
// @@protoc_insertion_point(global_scope)

@ -1,179 +0,0 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
#import "Empty.pb.h"
// @@protoc_insertion_point(imports)
@implementation RMTEmptyRoot
static PBExtensionRegistry* extensionRegistry = nil;
+ (PBExtensionRegistry*) extensionRegistry {
return extensionRegistry;
}
+ (void) initialize {
if (self == [RMTEmptyRoot class]) {
PBMutableExtensionRegistry* registry = [PBMutableExtensionRegistry registry];
[self registerAllExtensions:registry];
[ObjectivecDescriptorRoot registerAllExtensions:registry];
extensionRegistry = registry;
}
}
+ (void) registerAllExtensions:(PBMutableExtensionRegistry*) registry {
}
@end
@interface RMTEmpty ()
@end
@implementation RMTEmpty
- (instancetype) init {
if ((self = [super init])) {
}
return self;
}
static RMTEmpty* defaultRMTEmptyInstance = nil;
+ (void) initialize {
if (self == [RMTEmpty class]) {
defaultRMTEmptyInstance = [[RMTEmpty alloc] init];
}
}
+ (instancetype) defaultInstance {
return defaultRMTEmptyInstance;
}
- (instancetype) defaultInstance {
return defaultRMTEmptyInstance;
}
- (BOOL) isInitialized {
return YES;
}
- (void) writeToCodedOutputStream:(PBCodedOutputStream*) output {
[self.unknownFields writeToCodedOutputStream:output];
}
- (SInt32) serializedSize {
__block SInt32 size_ = memoizedSerializedSize;
if (size_ != -1) {
return size_;
}
size_ = 0;
size_ += self.unknownFields.serializedSize;
memoizedSerializedSize = size_;
return size_;
}
+ (RMTEmpty*) parseFromData:(NSData*) data {
return (RMTEmpty*)[[[RMTEmpty builder] mergeFromData:data] build];
}
+ (RMTEmpty*) parseFromData:(NSData*) data extensionRegistry:(PBExtensionRegistry*) extensionRegistry {
return (RMTEmpty*)[[[RMTEmpty builder] mergeFromData:data extensionRegistry:extensionRegistry] build];
}
+ (RMTEmpty*) parseFromInputStream:(NSInputStream*) input {
return (RMTEmpty*)[[[RMTEmpty builder] mergeFromInputStream:input] build];
}
+ (RMTEmpty*) parseFromInputStream:(NSInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry {
return (RMTEmpty*)[[[RMTEmpty builder] mergeFromInputStream:input extensionRegistry:extensionRegistry] build];
}
+ (RMTEmpty*) parseFromCodedInputStream:(PBCodedInputStream*) input {
return (RMTEmpty*)[[[RMTEmpty builder] mergeFromCodedInputStream:input] build];
}
+ (RMTEmpty*) parseFromCodedInputStream:(PBCodedInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry {
return (RMTEmpty*)[[[RMTEmpty builder] mergeFromCodedInputStream:input extensionRegistry:extensionRegistry] build];
}
+ (RMTEmptyBuilder*) builder {
return [[RMTEmptyBuilder alloc] init];
}
+ (RMTEmptyBuilder*) builderWithPrototype:(RMTEmpty*) prototype {
return [[RMTEmpty builder] mergeFrom:prototype];
}
- (RMTEmptyBuilder*) builder {
return [RMTEmpty builder];
}
- (RMTEmptyBuilder*) toBuilder {
return [RMTEmpty builderWithPrototype:self];
}
- (void) writeDescriptionTo:(NSMutableString*) output withIndent:(NSString*) indent {
[self.unknownFields writeDescriptionTo:output withIndent:indent];
}
- (BOOL) isEqual:(id)other {
if (other == self) {
return YES;
}
if (![other isKindOfClass:[RMTEmpty class]]) {
return NO;
}
RMTEmpty *otherMessage = other;
return
(self.unknownFields == otherMessage.unknownFields || (self.unknownFields != nil && [self.unknownFields isEqual:otherMessage.unknownFields]));
}
- (NSUInteger) hash {
__block NSUInteger hashCode = 7;
hashCode = hashCode * 31 + [self.unknownFields hash];
return hashCode;
}
@end
@interface RMTEmptyBuilder()
@property (strong) RMTEmpty* resultEmpty;
@end
@implementation RMTEmptyBuilder
@synthesize resultEmpty;
- (instancetype) init {
if ((self = [super init])) {
self.resultEmpty = [[RMTEmpty alloc] init];
}
return self;
}
- (PBGeneratedMessage*) internalGetResult {
return resultEmpty;
}
- (RMTEmptyBuilder*) clear {
self.resultEmpty = [[RMTEmpty alloc] init];
return self;
}
- (RMTEmptyBuilder*) clone {
return [RMTEmpty builderWithPrototype:resultEmpty];
}
- (RMTEmpty*) defaultInstance {
return [RMTEmpty defaultInstance];
}
- (RMTEmpty*) build {
[self checkInitialized];
return [self buildPartial];
}
- (RMTEmpty*) buildPartial {
RMTEmpty* returnMe = resultEmpty;
self.resultEmpty = nil;
return returnMe;
}
- (RMTEmptyBuilder*) mergeFrom:(RMTEmpty*) other {
if (other == [RMTEmpty defaultInstance]) {
return self;
}
[self mergeUnknownFields:other.unknownFields];
return self;
}
- (RMTEmptyBuilder*) mergeFromCodedInputStream:(PBCodedInputStream*) input {
return [self mergeFromCodedInputStream:input extensionRegistry:[PBExtensionRegistry emptyRegistry]];
}
- (RMTEmptyBuilder*) mergeFromCodedInputStream:(PBCodedInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry {
PBUnknownFieldSetBuilder* unknownFields = [PBUnknownFieldSet builderWithUnknownFields:self.unknownFields];
while (YES) {
SInt32 tag = [input readTag];
switch (tag) {
case 0:
[self setUnknownFields:[unknownFields build]];
return self;
default: {
if (![self parseUnknownField:input unknownFields:unknownFields extensionRegistry:extensionRegistry tag:tag]) {
[self setUnknownFields:[unknownFields build]];
return self;
}
break;
}
}
}
}
@end
// @@protoc_insertion_point(global_scope)

@ -0,0 +1,33 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: empty.proto
#import "GPBProtocolBuffers.h"
#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30000
#error This file was generated by a different version of protoc-gen-objc which is incompatible with your Protocol Buffer sources.
#endif
CF_EXTERN_C_BEGIN
@class RMTEmpty;
#pragma mark - RMTEmptyRoot
@interface RMTEmptyRoot : GPBRootObject
@end
#pragma mark - RMTEmpty
// An empty message that you can re-use to avoid defining duplicated empty
// messages in your project. A typical example is to use it as argument or the
// return value of a service API. For instance:
//
// service Foo {
// rpc Bar (grpc.testing.Empty) returns (grpc.testing.Empty) { };
// };
@interface RMTEmpty : GPBMessage
@end
CF_EXTERN_C_END

@ -0,0 +1,59 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: empty.proto
#import "GPBProtocolBuffers_RuntimeSupport.h"
#import "Empty.pbobjc.h"
#pragma mark - RMTEmptyRoot
@implementation RMTEmptyRoot
@end
static GPBFileDescriptor *RMTEmptyRoot_FileDescriptor(void) {
// This is called by +initialize so there is no need to worry
// about thread safety of the singleton.
static GPBFileDescriptor *descriptor = NULL;
if (!descriptor) {
descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"grpc.testing"
syntax:GPBFileSyntaxProto3];
}
return descriptor;
}
#pragma mark - RMTEmpty
@implementation RMTEmpty
typedef struct RMTEmpty_Storage {
uint32_t _has_storage_[0];
} RMTEmpty_Storage;
// This method is threadsafe because it is initially called
// in +initialize for each subclass.
+ (GPBDescriptor *)descriptor {
static GPBDescriptor *descriptor = NULL;
if (!descriptor) {
static GPBMessageFieldDescription fields[] = {
};
descriptor = [GPBDescriptor allocDescriptorForClass:[RMTEmpty class]
rootClass:[RMTEmptyRoot class]
file:RMTEmptyRoot_FileDescriptor()
fields:fields
fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription)
oneofs:NULL
oneofCount:0
enums:NULL
enumCount:0
ranges:NULL
rangeCount:0
storageSize:sizeof(RMTEmpty_Storage)
wireFormat:NO];
}
return descriptor;
}
@end

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

Loading…
Cancel
Save