Merge remote-tracking branch 'upstream/master'

Resolve a conflict between adding write buffer hint
and the change of API
Conflicts:
 src/cpp/server/async_server_context.cc
pull/388/head
Vijay Pai 10 years ago
commit 280744ec64
  1. 5
      .gitignore
  2. 265
      Makefile
  3. 102
      build.json
  4. 26
      examples/tips/README
  5. 2
      examples/tips/empty.proto
  6. 2
      examples/tips/label.proto
  7. 94
      examples/tips/main.cc
  8. 46
      examples/tips/publisher.cc
  9. 23
      examples/tips/publisher.h
  10. 69
      examples/tips/publisher_test.cc
  11. 2
      examples/tips/pubsub.proto
  12. 118
      examples/tips/subscriber.cc
  13. 38
      examples/tips/subscriber.h
  14. 157
      examples/tips/subscriber_test.cc
  15. 33
      include/grpc/grpc.h
  16. 6
      include/grpc/support/port_platform.h
  17. 13
      include/grpc/support/thd.h
  18. 42
      include/grpc/support/thd_posix.h
  19. 2
      src/core/httpcli/format_request.c
  20. 8
      src/core/iomgr/iomgr.c
  21. 3
      src/core/iomgr/pollset_kick.c
  22. 7
      src/core/iomgr/pollset_multipoller_with_poll_posix.c
  23. 12
      src/core/iomgr/pollset_posix.c
  24. 6
      src/core/iomgr/pollset_posix.h
  25. 2
      src/core/iomgr/wakeup_fd_eventfd.c
  26. 9
      src/core/iomgr/wakeup_fd_nospecial.c
  27. 8
      src/core/iomgr/wakeup_fd_pipe.c
  28. 2
      src/core/iomgr/wakeup_fd_pipe.h
  29. 14
      src/core/iomgr/wakeup_fd_posix.c
  30. 31
      src/core/iomgr/wakeup_fd_posix.h
  31. 35
      src/core/support/cpu_linux.c
  32. 2
      src/core/support/log_posix.c
  33. 8
      src/core/support/thd_posix.c
  34. 8
      src/core/support/thd_win32.c
  35. 28
      src/core/surface/call.c
  36. 4
      src/core/surface/channel.c
  37. 3
      src/core/surface/server.c
  38. 22
      src/core/tsi/ssl_transport_security.c
  39. 12
      src/cpp/client/channel.cc
  40. 2
      src/cpp/server/async_server.cc
  41. 11
      src/cpp/server/async_server_context.cc
  42. 2
      src/cpp/server/server.cc
  43. 12
      src/cpp/stream/stream_context.cc
  44. 2
      src/csharp/.gitignore
  45. 22
      src/csharp/README.md
  46. 113
      src/csharp/ext/grpc_csharp_ext.c
  47. 2
      src/node/.gitignore
  48. 24
      src/node/ext/call.cc
  49. 2
      src/node/ext/server.cc
  50. 27
      src/php/README.md
  51. 132
      src/php/ext/grpc/call.c
  52. 1
      src/php/ext/grpc/call.h
  53. 6
      src/php/ext/grpc/server.c
  54. 2
      src/php/lib/Grpc/AbstractSurfaceActiveCall.php
  55. 7
      src/php/lib/Grpc/ActiveCall.php
  56. 2
      src/php/lib/Grpc/ServerStreamingSurfaceActiveCall.php
  57. 20
      src/php/tests/generated_code/GeneratedCodeTest.php
  58. 55
      src/php/tests/interop/interop_client.php
  59. 9
      src/php/tests/unit_tests/CallTest.php
  60. 96
      src/php/tests/unit_tests/EndToEndTest.php
  61. 94
      src/php/tests/unit_tests/SecureEndToEndTest.php
  62. 2
      src/php/tests/unit_tests/TimevalTest.php
  63. 6
      src/php/tests/util/port_picker.php
  64. 19
      src/python/src/_adapter/_call.c
  65. 11
      src/python/src/_adapter/_links_test.py
  66. 2
      src/python/src/_adapter/_lonely_rear_link_test.py
  67. 2
      src/python/src/_adapter/_server.c
  68. 3
      src/python/src/_adapter/fore.py
  69. 37
      src/python/src/_framework/base/interfaces.py
  70. 44
      src/python/src/_framework/base/interfaces_test.py
  71. 25
      src/python/src/_framework/base/packets/_ends.py
  72. 2
      src/python/src/_framework/base/packets/_ingestion.py
  73. 5
      src/python/src/_framework/base/packets/_interfaces.py
  74. 82
      src/python/src/_framework/base/packets/_termination.py
  75. 34
      src/python/src/_framework/base/packets/_transmission.py
  76. 7
      src/python/src/_framework/base/packets/packets.py
  77. 15
      src/python/src/_framework/base/util.py
  78. 11
      src/python/src/_framework/face/_calls.py
  79. 26
      src/python/src/_framework/face/_control.py
  80. 43
      src/python/src/_framework/face/interfaces.py
  81. 24
      src/python/src/_framework/face/testing/event_invocation_synchronous_event_service_test_case.py
  82. 10
      src/ruby/Rakefile
  83. 44
      src/ruby/bin/apis/google/protobuf/empty.rb
  84. 278
      src/ruby/bin/apis/pubsub_demo.rb
  85. 174
      src/ruby/bin/apis/tech/pubsub/proto/pubsub.rb
  86. 103
      src/ruby/bin/apis/tech/pubsub/proto/pubsub_services.rb
  87. 178
      src/ruby/bin/interop/interop_client.rb
  88. 5
      src/ruby/bin/interop/test/cpp/interop/messages.rb
  89. 0
      src/ruby/bin/math.rb
  90. 0
      src/ruby/bin/math_services.rb
  91. 18
      src/ruby/ext/grpc/rb_call.c
  92. 3
      src/ruby/ext/grpc/rb_channel.c
  93. 2
      src/ruby/ext/grpc/rb_server.c
  94. 10
      src/ruby/grpc.gemspec
  95. 2
      src/ruby/lib/grpc.rb
  96. 69
      src/ruby/lib/grpc/auth/compute_engine.rb
  97. 68
      src/ruby/lib/grpc/auth/service_account.rb
  98. 67
      src/ruby/lib/grpc/auth/signet.rb
  99. 163
      src/ruby/spec/auth/apply_auth_examples.rb
  100. 108
      src/ruby/spec/auth/compute_engine_spec.rb
  101. Some files were not shown because too many files have changed in this diff Show More

5
.gitignore vendored

@ -17,6 +17,11 @@ coverage
# python compiled objects # python compiled objects
*.pyc *.pyc
#eclipse project files
.cproject
.project
.settings
# cache for run_tests.py # cache for run_tests.py
.run_tests_cache .run_tests_cache

File diff suppressed because one or more lines are too long

@ -220,8 +220,6 @@
"include/grpc/support/sync_posix.h", "include/grpc/support/sync_posix.h",
"include/grpc/support/sync_win32.h", "include/grpc/support/sync_win32.h",
"include/grpc/support/thd.h", "include/grpc/support/thd.h",
"include/grpc/support/thd_posix.h",
"include/grpc/support/thd_win32.h",
"include/grpc/support/time.h", "include/grpc/support/time.h",
"include/grpc/support/time_posix.h", "include/grpc/support/time_posix.h",
"include/grpc/support/time_win32.h", "include/grpc/support/time_win32.h",
@ -433,13 +431,26 @@
"examples/tips/label.proto", "examples/tips/label.proto",
"examples/tips/empty.proto", "examples/tips/empty.proto",
"examples/tips/pubsub.proto", "examples/tips/pubsub.proto",
"examples/tips/client.cc" "examples/tips/publisher.cc",
"examples/tips/subscriber.cc"
], ],
"deps": [ "deps": [
"grpc++", "grpc++",
"grpc", "grpc",
"gpr" "gpr"
] ]
},
{
"name": "grpc_csharp_ext",
"build": "all",
"language": "c",
"deps": [
"gpr",
"grpc"
],
"src": [
"src/csharp/ext/grpc_csharp_ext.c"
]
} }
], ],
"targets": [ "targets": [
@ -1568,31 +1579,32 @@
"run": false "run": false
}, },
{ {
"name": "qps_client", "name": "tips_client",
"build": "test", "build": "test",
"language": "c++", "language": "c++",
"src": [ "src": [
"test/cpp/qps/qpstest.proto", "examples/tips/main.cc"
"test/cpp/qps/client.cc"
], ],
"deps": [ "deps": [
"tips_client_lib",
"grpc++_test_util", "grpc++_test_util",
"grpc_test_util", "grpc_test_util",
"grpc++", "grpc++",
"grpc", "grpc",
"gpr_test_util", "gpr_test_util",
"gpr" "gpr"
] ],
"run": false
}, },
{ {
"name": "qps_server", "name": "tips_publisher_test",
"build": "test", "build": "test",
"language": "c++", "language": "c++",
"src": [ "src": [
"test/cpp/qps/qpstest.proto", "examples/tips/publisher_test.cc"
"test/cpp/qps/server.cc"
], ],
"deps": [ "deps": [
"tips_client_lib",
"grpc++_test_util", "grpc++_test_util",
"grpc_test_util", "grpc_test_util",
"grpc++", "grpc++",
@ -1602,30 +1614,32 @@
] ]
}, },
{ {
"name": "ruby_plugin", "name": "tips_subscriber_test",
"build": "protoc", "build": "test",
"language": "c++", "language": "c++",
"headers": [
"src/compiler/cpp_generator.h",
"src/compiler/cpp_generator_helpers-inl.h",
"src/compiler/cpp_generator_map-inl.h",
"src/compiler/cpp_generator_string-inl.h"
],
"src": [ "src": [
"src/compiler/ruby_generator.cc", "examples/tips/subscriber_test.cc"
"src/compiler/ruby_plugin.cc"
], ],
"deps": [], "deps": [
"secure": false "tips_client_lib",
"grpc++_test_util",
"grpc_test_util",
"grpc++",
"grpc",
"gpr_test_util",
"gpr"
]
}, },
{ {
"name": "status_test", "name": "qps_client",
"build": "test", "build": "test",
"language": "c++", "language": "c++",
"src": [ "src": [
"test/cpp/util/status_test.cc" "test/cpp/qps/qpstest.proto",
"test/cpp/qps/client.cc"
], ],
"deps": [ "deps": [
"grpc++_test_util",
"grpc_test_util", "grpc_test_util",
"grpc++", "grpc++",
"grpc", "grpc",
@ -1634,11 +1648,12 @@
] ]
}, },
{ {
"name": "sync_client_async_server_test", "name": "qps_server",
"build": "test", "build": "test",
"language": "c++", "language": "c++",
"src": [ "src": [
"test/cpp/end2end/sync_client_async_server_test.cc" "test/cpp/qps/qpstest.proto",
"test/cpp/qps/server.cc"
], ],
"deps": [ "deps": [
"grpc++_test_util", "grpc++_test_util",
@ -1650,11 +1665,28 @@
] ]
}, },
{ {
"name": "thread_pool_test", "name": "ruby_plugin",
"build": "protoc",
"language": "c++",
"headers": [
"src/compiler/cpp_generator.h",
"src/compiler/cpp_generator_helpers-inl.h",
"src/compiler/cpp_generator_map-inl.h",
"src/compiler/cpp_generator_string-inl.h"
],
"src": [
"src/compiler/ruby_generator.cc",
"src/compiler/ruby_plugin.cc"
],
"deps": [],
"secure": false
},
{
"name": "status_test",
"build": "test", "build": "test",
"language": "c++", "language": "c++",
"src": [ "src": [
"test/cpp/server/thread_pool_test.cc" "test/cpp/util/status_test.cc"
], ],
"deps": [ "deps": [
"grpc_test_util", "grpc_test_util",
@ -1665,33 +1697,29 @@
] ]
}, },
{ {
"name": "tips_client", "name": "sync_client_async_server_test",
"build": "test", "build": "test",
"language": "c++", "language": "c++",
"src": [ "src": [
"examples/tips/client_main.cc" "test/cpp/end2end/sync_client_async_server_test.cc"
], ],
"deps": [ "deps": [
"tips_client_lib",
"grpc++_test_util", "grpc++_test_util",
"grpc_test_util", "grpc_test_util",
"grpc++", "grpc++",
"grpc", "grpc",
"gpr_test_util", "gpr_test_util",
"gpr" "gpr"
], ]
"run": false
}, },
{ {
"name": "tips_client_test", "name": "thread_pool_test",
"build": "test", "build": "test",
"language": "c++", "language": "c++",
"src": [ "src": [
"examples/tips/client_test.cc" "test/cpp/server/thread_pool_test.cc"
], ],
"deps": [ "deps": [
"tips_client_lib",
"grpc++_test_util",
"grpc_test_util", "grpc_test_util",
"grpc++", "grpc++",
"grpc", "grpc",

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

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

@ -1,3 +1,5 @@
// This file will be moved to a new location.
// Labels provide a way to associate user-defined metadata with various // Labels provide a way to associate user-defined metadata with various
// objects. Labels may be used to organize objects into non-hierarchical // objects. Labels may be used to organize objects into non-hierarchical
// groups; think metadata tags attached to mp3s. // groups; think metadata tags attached to mp3s.

@ -46,18 +46,30 @@
#include <grpc++/credentials.h> #include <grpc++/credentials.h>
#include <grpc++/status.h> #include <grpc++/status.h>
#include "examples/tips/client.h" #include "examples/tips/publisher.h"
#include "examples/tips/subscriber.h"
#include "test/cpp/util/create_test_channel.h" #include "test/cpp/util/create_test_channel.h"
DEFINE_int32(server_port, 443, "Server port."); DEFINE_int32(server_port, 443, "Server port.");
DEFINE_string(server_host, DEFINE_string(server_host,
"pubsub-staging.googleapis.com", "Server host to connect to"); "pubsub-staging.googleapis.com", "Server host to connect to");
DEFINE_string(project_id, "", "GCE project id such as stoked-keyword-656");
DEFINE_string(service_account_key_file, "", DEFINE_string(service_account_key_file, "",
"Path to service account json key file."); "Path to service account json key file.");
DEFINE_string(oauth_scope, "", "Scope for OAuth tokens."); DEFINE_string(oauth_scope,
"https://www.googleapis.com/auth/cloud-platform",
"Scope for OAuth tokens.");
namespace {
const char kTopic[] = "testtopics";
const char kSubscriptionName[] = "testsubscription";
const char kMessageData[] = "Test Data";
} // namespace
grpc::string GetServiceAccountJsonKey() { grpc::string GetServiceAccountJsonKey() {
static grpc::string json_key; grpc::string json_key;
if (json_key.empty()) { if (json_key.empty()) {
std::ifstream json_key_file(FLAGS_service_account_key_file); std::ifstream json_key_file(FLAGS_service_account_key_file);
std::stringstream key_stream; std::stringstream key_stream;
@ -72,10 +84,7 @@ int main(int argc, char** argv) {
google::ParseCommandLineFlags(&argc, &argv, true); google::ParseCommandLineFlags(&argc, &argv, true);
gpr_log(GPR_INFO, "Start TIPS client"); gpr_log(GPR_INFO, "Start TIPS client");
const int host_port_buf_size = 1024; std::ostringstream ss;
char host_port[host_port_buf_size];
snprintf(host_port, host_port_buf_size, "%s:%d", FLAGS_server_host.c_str(),
FLAGS_server_port);
std::unique_ptr<grpc::Credentials> creds; std::unique_ptr<grpc::Credentials> creds;
if (FLAGS_service_account_key_file != "") { if (FLAGS_service_account_key_file != "") {
@ -86,28 +95,83 @@ int main(int argc, char** argv) {
creds = grpc::CredentialsFactory::ComputeEngineCredentials(); creds = grpc::CredentialsFactory::ComputeEngineCredentials();
} }
ss << FLAGS_server_host << ":" << FLAGS_server_port;
std::shared_ptr<grpc::ChannelInterface> channel( std::shared_ptr<grpc::ChannelInterface> channel(
grpc::CreateTestChannel( grpc::CreateTestChannel(
host_port, ss.str(),
FLAGS_server_host, FLAGS_server_host,
true, // enable SSL true, // enable SSL
true, // use prod roots true, // use prod roots
creds)); creds));
grpc::examples::tips::Client client(channel); grpc::examples::tips::Publisher publisher(channel);
grpc::examples::tips::Subscriber subscriber(channel);
GPR_ASSERT(FLAGS_project_id != "");
ss.str("");
ss << "/topics/" << FLAGS_project_id << "/" << kTopic;
grpc::string topic = ss.str();
ss.str("");
ss << FLAGS_project_id << "/" << kSubscriptionName;
grpc::string subscription_name = ss.str();
// Clean up test topic and subcription if they exist before.
grpc::string subscription_topic;
if (subscriber.GetSubscription(
subscription_name, &subscription_topic).IsOk()) {
subscriber.DeleteSubscription(subscription_name);
}
if (publisher.GetTopic(topic).IsOk()) publisher.DeleteTopic(topic);
grpc::Status s = publisher.CreateTopic(topic);
gpr_log(GPR_INFO, "Create topic returns code %d, %s",
s.code(), s.details().c_str());
GPR_ASSERT(s.IsOk());
grpc::Status s = client.CreateTopic("/topics/stoked-keyword-656/testtopics"); s = publisher.GetTopic(topic);
gpr_log(GPR_INFO, "return code %d, %s", s.code(), s.details().c_str()); gpr_log(GPR_INFO, "Get topic returns code %d, %s",
s.code(), s.details().c_str());
GPR_ASSERT(s.IsOk()); GPR_ASSERT(s.IsOk());
s = client.GetTopic("/topics/stoked-keyword-656/testtopics"); std::vector<grpc::string> topics;
gpr_log(GPR_INFO, "return code %d, %s", s.code(), s.details().c_str()); s = publisher.ListTopics(FLAGS_project_id, &topics);
gpr_log(GPR_INFO, "List topic returns code %d, %s",
s.code(), s.details().c_str());
bool topic_found = false;
for (unsigned int i = 0; i < topics.size(); i++) {
if (topics[i] == topic) topic_found = true;
gpr_log(GPR_INFO, "topic: %s", topics[i].c_str());
}
GPR_ASSERT(s.IsOk());
GPR_ASSERT(topic_found);
s = subscriber.CreateSubscription(topic, subscription_name);
gpr_log(GPR_INFO, "create subscrption returns code %d, %s",
s.code(), s.details().c_str());
GPR_ASSERT(s.IsOk());
s = publisher.Publish(topic, kMessageData);
gpr_log(GPR_INFO, "Publish %s returns code %d, %s",
kMessageData, s.code(), s.details().c_str());
GPR_ASSERT(s.IsOk());
grpc::string data;
s = subscriber.Pull(subscription_name, &data);
gpr_log(GPR_INFO, "Pull %s", data.c_str());
s = subscriber.DeleteSubscription(subscription_name);
gpr_log(GPR_INFO, "Delete subscription returns code %d, %s",
s.code(), s.details().c_str());
GPR_ASSERT(s.IsOk()); GPR_ASSERT(s.IsOk());
s = client.DeleteTopic("/topics/stoked-keyword-656/testtopics"); s = publisher.DeleteTopic(topic);
gpr_log(GPR_INFO, "return code %d, %s", s.code(), s.details().c_str()); gpr_log(GPR_INFO, "Delete topic returns code %d, %s",
s.code(), s.details().c_str());
GPR_ASSERT(s.IsOk()); GPR_ASSERT(s.IsOk());
subscriber.Shutdown();
publisher.Shutdown();
channel.reset(); channel.reset();
grpc_shutdown(); grpc_shutdown();
return 0; return 0;

@ -31,9 +31,11 @@
* *
*/ */
#include <sstream>
#include <grpc++/client_context.h> #include <grpc++/client_context.h>
#include "examples/tips/client.h" #include "examples/tips/publisher.h"
using tech::pubsub::Topic; using tech::pubsub::Topic;
using tech::pubsub::DeleteTopicRequest; using tech::pubsub::DeleteTopicRequest;
@ -41,16 +43,22 @@ using tech::pubsub::GetTopicRequest;
using tech::pubsub::PublisherService; using tech::pubsub::PublisherService;
using tech::pubsub::ListTopicsRequest; using tech::pubsub::ListTopicsRequest;
using tech::pubsub::ListTopicsResponse; using tech::pubsub::ListTopicsResponse;
using tech::pubsub::PublishRequest;
using tech::pubsub::PubsubMessage;
namespace grpc { namespace grpc {
namespace examples { namespace examples {
namespace tips { namespace tips {
Client::Client(std::shared_ptr<ChannelInterface> channel) Publisher::Publisher(std::shared_ptr<ChannelInterface> channel)
: stub_(PublisherService::NewStub(channel)) { : stub_(PublisherService::NewStub(channel)) {
} }
Status Client::CreateTopic(grpc::string topic) { void Publisher::Shutdown() {
stub_.reset();
}
Status Publisher::CreateTopic(const grpc::string& topic) {
Topic request; Topic request;
Topic response; Topic response;
request.set_name(topic); request.set_name(topic);
@ -59,15 +67,28 @@ Status Client::CreateTopic(grpc::string topic) {
return stub_->CreateTopic(&context, request, &response); return stub_->CreateTopic(&context, request, &response);
} }
Status Client::ListTopics() { Status Publisher::ListTopics(const grpc::string& project_id,
std::vector<grpc::string>* topics) {
ListTopicsRequest request; ListTopicsRequest request;
ListTopicsResponse response; ListTopicsResponse response;
ClientContext context; ClientContext context;
return stub_->ListTopics(&context, request, &response); std::ostringstream ss;
ss << "cloud.googleapis.com/project in (/projects/" << project_id << ")";
request.set_query(ss.str());
Status s = stub_->ListTopics(&context, request, &response);
tech::pubsub::Topic topic;
for (int i = 0; i < response.topic_size(); i++) {
topic = response.topic(i);
topics->push_back(topic.name());
}
return s;
} }
Status Client::GetTopic(grpc::string topic) { Status Publisher::GetTopic(const grpc::string& topic) {
GetTopicRequest request; GetTopicRequest request;
Topic response; Topic response;
ClientContext context; ClientContext context;
@ -77,7 +98,7 @@ Status Client::GetTopic(grpc::string topic) {
return stub_->GetTopic(&context, request, &response); return stub_->GetTopic(&context, request, &response);
} }
Status Client::DeleteTopic(grpc::string topic) { Status Publisher::DeleteTopic(const grpc::string& topic) {
DeleteTopicRequest request; DeleteTopicRequest request;
proto2::Empty response; proto2::Empty response;
ClientContext context; ClientContext context;
@ -87,6 +108,17 @@ Status Client::DeleteTopic(grpc::string topic) {
return stub_->DeleteTopic(&context, request, &response); return stub_->DeleteTopic(&context, request, &response);
} }
Status Publisher::Publish(const grpc::string& topic, const grpc::string& data) {
PublishRequest request;
proto2::Empty response;
ClientContext context;
request.mutable_message()->set_data(data);
request.set_topic(topic);
return stub_->Publish(&context, request, &response);
}
} // namespace tips } // namespace tips
} // namespace examples } // namespace examples
} // namespace grpc } // namespace grpc

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

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

@ -1,3 +1,5 @@
// This file will be moved to a new location.
// Specification of the Pubsub API. // Specification of the Pubsub API.
syntax = "proto2"; syntax = "proto2";

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

@ -31,14 +31,38 @@
* *
*/ */
#ifndef __GRPC_SUPPORT_THD_WIN32_H__ #ifndef __GRPCPP_EXAMPLES_TIPS_SUBSCRIBER_H_
#define __GRPC_SUPPORT_THD_WIN32_H__ #define __GRPCPP_EXAMPLES_TIPS_SUBSCRIBER_H_
/* Win32 variant of gpr_thd_platform.h */ #include <grpc++/channel_interface.h>
#include <grpc++/status.h>
#include <windows.h> #include "examples/tips/pubsub.pb.h"
#include <grpc/support/atm.h>
typedef int gpr_thd_id; namespace grpc {
namespace examples {
namespace tips {
#endif /* __GRPC_SUPPORT_THD_WIN32_H__ */ class Subscriber {
public:
Subscriber(std::shared_ptr<ChannelInterface> channel);
void Shutdown();
Status CreateSubscription(const grpc::string& topic,
const grpc::string& name);
Status GetSubscription(const grpc::string& name, grpc::string* topic);
Status DeleteSubscription(const grpc::string& name);
Status Pull(const grpc::string& name, grpc::string* data);
private:
std::unique_ptr<tech::pubsub::SubscriberService::Stub> stub_;
};
} // namespace tips
} // namespace examples
} // namespace grpc
#endif // __GRPCPP_EXAMPLES_TIPS_SUBSCRIBER_H_

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

@ -275,8 +275,9 @@ void grpc_completion_queue_destroy(grpc_completion_queue *cq);
/* Create a call given a grpc_channel, in order to call 'method'. The request /* Create a call given a grpc_channel, in order to call 'method'. The request
is not sent until grpc_call_invoke is called. All completions are sent to is not sent until grpc_call_invoke is called. All completions are sent to
'completion_queue'. */ 'completion_queue'. */
grpc_call *grpc_channel_create_call(grpc_channel *channel, const char *method, grpc_call *grpc_channel_create_call_old(grpc_channel *channel,
const char *host, gpr_timespec deadline); const char *method, const char *host,
gpr_timespec deadline);
/* Create a client channel */ /* Create a client channel */
grpc_channel *grpc_channel_create(const char *target, grpc_channel *grpc_channel_create(const char *target,
@ -307,7 +308,8 @@ void grpc_channel_destroy(grpc_channel *channel);
REQUIRES: grpc_call_start_invoke/grpc_call_server_end_initial_metadata have REQUIRES: grpc_call_start_invoke/grpc_call_server_end_initial_metadata have
not been called on this call. not been called on this call.
Produces no events. */ Produces no events. */
grpc_call_error grpc_call_add_metadata(grpc_call *call, grpc_metadata *metadata, grpc_call_error grpc_call_add_metadata_old(grpc_call *call,
grpc_metadata *metadata,
gpr_uint32 flags); gpr_uint32 flags);
/* Invoke the RPC. Starts sending metadata and request headers on the wire. /* Invoke the RPC. Starts sending metadata and request headers on the wire.
@ -319,9 +321,9 @@ grpc_call_error grpc_call_add_metadata(grpc_call *call, grpc_metadata *metadata,
Produces a GRPC_FINISHED event with finished_tag when the call has been Produces a GRPC_FINISHED event with finished_tag when the call has been
completed (there may be other events for the call pending at this completed (there may be other events for the call pending at this
time) */ time) */
grpc_call_error grpc_call_invoke(grpc_call *call, grpc_completion_queue *cq, grpc_call_error grpc_call_invoke_old(grpc_call *call, grpc_completion_queue *cq,
void *metadata_read_tag, void *finished_tag, void *metadata_read_tag,
gpr_uint32 flags); void *finished_tag, gpr_uint32 flags);
/* Accept an incoming RPC, binding a completion queue to it. /* Accept an incoming RPC, binding a completion queue to it.
To be called before sending or receiving messages. To be called before sending or receiving messages.
@ -330,7 +332,7 @@ grpc_call_error grpc_call_invoke(grpc_call *call, grpc_completion_queue *cq,
Produces a GRPC_FINISHED event with finished_tag when the call has been Produces a GRPC_FINISHED event with finished_tag when the call has been
completed (there may be other events for the call pending at this completed (there may be other events for the call pending at this
time) */ time) */
grpc_call_error grpc_call_server_accept(grpc_call *call, grpc_call_error grpc_call_server_accept_old(grpc_call *call,
grpc_completion_queue *cq, grpc_completion_queue *cq,
void *finished_tag); void *finished_tag);
@ -340,7 +342,7 @@ grpc_call_error grpc_call_server_accept(grpc_call *call,
REQUIRES: Can be called at most once per call. REQUIRES: Can be called at most once per call.
Can only be called on the server. Can only be called on the server.
Must be called after grpc_call_server_accept */ Must be called after grpc_call_server_accept */
grpc_call_error grpc_call_server_end_initial_metadata(grpc_call *call, grpc_call_error grpc_call_server_end_initial_metadata_old(grpc_call *call,
gpr_uint32 flags); gpr_uint32 flags);
/* Called by clients to cancel an RPC on the server. /* Called by clients to cancel an RPC on the server.
@ -370,9 +372,9 @@ grpc_call_error grpc_call_cancel_with_status(grpc_call *call,
grpc_call_server_end_of_initial_metadata must have been called grpc_call_server_end_of_initial_metadata must have been called
successfully. successfully.
Produces a GRPC_WRITE_ACCEPTED event. */ Produces a GRPC_WRITE_ACCEPTED event. */
grpc_call_error grpc_call_start_write(grpc_call *call, grpc_call_error grpc_call_start_write_old(grpc_call *call,
grpc_byte_buffer *byte_buffer, void *tag, grpc_byte_buffer *byte_buffer,
gpr_uint32 flags); void *tag, gpr_uint32 flags);
/* Queue a status for writing. /* Queue a status for writing.
REQUIRES: No other writes are pending on the call. REQUIRES: No other writes are pending on the call.
@ -380,7 +382,7 @@ grpc_call_error grpc_call_start_write(grpc_call *call,
call prior to calling this. call prior to calling this.
Only callable on the server. Only callable on the server.
Produces a GRPC_FINISH_ACCEPTED event when the status is sent. */ Produces a GRPC_FINISH_ACCEPTED event when the status is sent. */
grpc_call_error grpc_call_start_write_status(grpc_call *call, grpc_call_error grpc_call_start_write_status_old(grpc_call *call,
grpc_status_code status_code, grpc_status_code status_code,
const char *status_message, const char *status_message,
void *tag); void *tag);
@ -390,7 +392,7 @@ grpc_call_error grpc_call_start_write_status(grpc_call *call,
Only callable on the client. Only callable on the client.
Produces a GRPC_FINISH_ACCEPTED event when all bytes for the call have passed Produces a GRPC_FINISH_ACCEPTED event when all bytes for the call have passed
outgoing flow control. */ outgoing flow control. */
grpc_call_error grpc_call_writes_done(grpc_call *call, void *tag); grpc_call_error grpc_call_writes_done_old(grpc_call *call, void *tag);
/* Initiate a read on a call. Output event contains a byte buffer with the /* Initiate a read on a call. Output event contains a byte buffer with the
result of the read. result of the read.
@ -402,7 +404,7 @@ grpc_call_error grpc_call_writes_done(grpc_call *call, void *tag);
On the server: On the server:
grpc_call_server_accept must be called before calling this. grpc_call_server_accept must be called before calling this.
Produces a single GRPC_READ event. */ Produces a single GRPC_READ event. */
grpc_call_error grpc_call_start_read(grpc_call *call, void *tag); grpc_call_error grpc_call_start_read_old(grpc_call *call, void *tag);
/* Destroy a call. */ /* Destroy a call. */
void grpc_call_destroy(grpc_call *call); void grpc_call_destroy(grpc_call *call);
@ -414,7 +416,8 @@ void grpc_call_destroy(grpc_call *call);
tag_cancel. tag_cancel.
REQUIRES: Server must not have been shutdown. REQUIRES: Server must not have been shutdown.
NOTE: calling this is the only way to obtain GRPC_SERVER_RPC_NEW events. */ NOTE: calling this is the only way to obtain GRPC_SERVER_RPC_NEW events. */
grpc_call_error grpc_server_request_call(grpc_server *server, void *tag_new); grpc_call_error grpc_server_request_call_old(grpc_server *server,
void *tag_new);
/* Create a server */ /* Create a server */
grpc_server *grpc_server_create(grpc_completion_queue *cq, grpc_server *grpc_server_create(grpc_completion_queue *cq,

@ -56,6 +56,8 @@
#define GPR_CPU_LINUX 1 #define GPR_CPU_LINUX 1
#define GPR_GCC_SYNC 1 #define GPR_GCC_SYNC 1
#define GPR_POSIX_MULTIPOLL_WITH_POLL 1 #define GPR_POSIX_MULTIPOLL_WITH_POLL 1
#define GPR_POSIX_WAKEUP_FD 1
#define GPR_LINUX_EVENTFD 1
#define GPR_POSIX_SOCKET 1 #define GPR_POSIX_SOCKET 1
#define GPR_POSIX_SOCKETADDR 1 #define GPR_POSIX_SOCKETADDR 1
#define GPR_POSIX_SOCKETUTILS 1 #define GPR_POSIX_SOCKETUTILS 1
@ -68,7 +70,7 @@
#define GPR_GCC_ATOMIC 1 #define GPR_GCC_ATOMIC 1
#define GPR_LINUX 1 #define GPR_LINUX 1
#define GPR_POSIX_MULTIPOLL_WITH_POLL 1 #define GPR_POSIX_MULTIPOLL_WITH_POLL 1
#define GPR_POSIX_HAS_SPECIAL_WAKEUP_FD 1 #define GPR_POSIX_WAKEUP_FD 1
#define GPR_LINUX_EVENTFD 1 #define GPR_LINUX_EVENTFD 1
#define GPR_POSIX_SOCKET 1 #define GPR_POSIX_SOCKET 1
#define GPR_POSIX_SOCKETADDR 1 #define GPR_POSIX_SOCKETADDR 1
@ -86,6 +88,8 @@
#define GPR_GCC_ATOMIC 1 #define GPR_GCC_ATOMIC 1
#define GPR_POSIX_LOG 1 #define GPR_POSIX_LOG 1
#define GPR_POSIX_MULTIPOLL_WITH_POLL 1 #define GPR_POSIX_MULTIPOLL_WITH_POLL 1
#define GPR_POSIX_WAKEUP_FD 1
#define GPR_POSIX_NO_SPECIAL_WAKEUP_FD 1
#define GPR_POSIX_SOCKET 1 #define GPR_POSIX_SOCKET 1
#define GPR_POSIX_SOCKETADDR 1 #define GPR_POSIX_SOCKETADDR 1
#define GPR_POSIX_SOCKETUTILS 1 #define GPR_POSIX_SOCKETUTILS 1

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

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

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

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

@ -138,15 +138,18 @@ void grpc_pollset_kick_kick(grpc_pollset_kick_state *kick_state) {
} }
void grpc_pollset_kick_global_init_fallback_fd(void) { void grpc_pollset_kick_global_init_fallback_fd(void) {
gpr_mu_init(&fd_freelist_mu);
grpc_wakeup_fd_global_init_force_fallback(); grpc_wakeup_fd_global_init_force_fallback();
} }
void grpc_pollset_kick_global_init(void) { void grpc_pollset_kick_global_init(void) {
gpr_mu_init(&fd_freelist_mu);
grpc_wakeup_fd_global_init(); grpc_wakeup_fd_global_init();
} }
void grpc_pollset_kick_global_destroy(void) { void grpc_pollset_kick_global_destroy(void) {
grpc_wakeup_fd_global_destroy(); grpc_wakeup_fd_global_destroy();
gpr_mu_destroy(&fd_freelist_mu);
} }

@ -147,8 +147,6 @@ static int multipoll_with_poll_pollset_maybe_work(
grpc_fd_unref(h->fds[i]); grpc_fd_unref(h->fds[i]);
} else { } else {
h->fds[nf++] = h->fds[i]; h->fds[nf++] = h->fds[i];
h->pfds[np].events =
grpc_fd_begin_poll(h->fds[i], pollset, POLLIN, POLLOUT);
h->selfds[np] = h->fds[i]; h->selfds[np] = h->fds[i];
h->pfds[np].fd = h->fds[i]->fd; h->pfds[np].fd = h->fds[i]->fd;
h->pfds[np].revents = 0; h->pfds[np].revents = 0;
@ -168,6 +166,11 @@ static int multipoll_with_poll_pollset_maybe_work(
pollset->counter = 1; pollset->counter = 1;
gpr_mu_unlock(&pollset->mu); gpr_mu_unlock(&pollset->mu);
for (i = 1; i < np; i++) {
h->pfds[i].events =
grpc_fd_begin_poll(h->selfds[i], pollset, POLLIN, POLLOUT);
}
r = poll(h->pfds, h->pfd_count, timeout); r = poll(h->pfds, h->pfd_count, timeout);
if (r < 0) { if (r < 0) {
if (errno != EINTR) { if (errno != EINTR) {

@ -75,11 +75,14 @@ static void backup_poller(void *p) {
} }
void grpc_pollset_kick(grpc_pollset *p) { void grpc_pollset_kick(grpc_pollset *p) {
if (!p->counter) return; if (p->counter) {
grpc_pollset_kick_kick(&p->kick_state); grpc_pollset_kick_kick(&p->kick_state);
} }
}
void grpc_pollset_force_kick(grpc_pollset *p) { grpc_pollset_kick(p); } void grpc_pollset_force_kick(grpc_pollset *p) {
grpc_pollset_kick_kick(&p->kick_state);
}
/* global state management */ /* global state management */
@ -244,11 +247,12 @@ static int unary_poll_pollset_maybe_work(grpc_pollset *pollset,
pfd[0].events = POLLIN; pfd[0].events = POLLIN;
pfd[0].revents = 0; pfd[0].revents = 0;
pfd[1].fd = fd->fd; pfd[1].fd = fd->fd;
pfd[1].events = grpc_fd_begin_poll(fd, pollset, POLLIN, POLLOUT);
pfd[1].revents = 0; pfd[1].revents = 0;
pollset->counter = 1; pollset->counter = 1;
gpr_mu_unlock(&pollset->mu); gpr_mu_unlock(&pollset->mu);
pfd[1].events = grpc_fd_begin_poll(fd, pollset, POLLIN, POLLOUT);
r = poll(pfd, GPR_ARRAY_SIZE(pfd), timeout); r = poll(pfd, GPR_ARRAY_SIZE(pfd), timeout);
if (r < 0) { if (r < 0) {
if (errno != EINTR) { if (errno != EINTR) {
@ -269,9 +273,9 @@ static int unary_poll_pollset_maybe_work(grpc_pollset *pollset,
} }
grpc_pollset_kick_post_poll(&pollset->kick_state); grpc_pollset_kick_post_poll(&pollset->kick_state);
grpc_fd_end_poll(fd, pollset);
gpr_mu_lock(&pollset->mu); gpr_mu_lock(&pollset->mu);
grpc_fd_end_poll(fd, pollset);
pollset->counter = 0; pollset->counter = 0;
gpr_cv_broadcast(&pollset->cv); gpr_cv_broadcast(&pollset->cv);
return 1; return 1;

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

@ -74,7 +74,7 @@ static int eventfd_check_availability(void) {
return 1; return 1;
} }
const grpc_wakeup_fd_vtable specialized_wakeup_fd_vtable = { const grpc_wakeup_fd_vtable grpc_specialized_wakeup_fd_vtable = {
eventfd_create, eventfd_consume, eventfd_wakeup, eventfd_destroy, eventfd_create, eventfd_consume, eventfd_wakeup, eventfd_destroy,
eventfd_check_availability eventfd_check_availability
}; };

@ -38,16 +38,17 @@
#include <grpc/support/port_platform.h> #include <grpc/support/port_platform.h>
#ifndef GPR_POSIX_HAS_SPECIAL_WAKEUP_FD #ifdef GPR_POSIX_NO_SPECIAL_WAKEUP_FD
#include "src/core/iomgr/wakeup_fd.h" #include "src/core/iomgr/wakeup_fd_posix.h"
#include <stddef.h>
static int check_availability_invalid(void) { static int check_availability_invalid(void) {
return 0; return 0;
} }
const grpc_wakeup_fd_vtable specialized_wakeup_fd_vtable = { const grpc_wakeup_fd_vtable grpc_specialized_wakeup_fd_vtable = {
NULL, NULL, NULL, NULL, check_availability_invalid NULL, NULL, NULL, NULL, check_availability_invalid
}; };
#endif /* GPR_POSIX_HAS_SPECIAL_WAKEUP */ #endif /* GPR_POSIX_NO_SPECIAL_WAKEUP_FD */

@ -31,7 +31,10 @@
* *
*/ */
/* TODO(klempner): Allow this code to be disabled. */ #include <grpc/support/port_platform.h>
#ifdef GPR_POSIX_WAKEUP_FD
#include "src/core/iomgr/wakeup_fd_posix.h" #include "src/core/iomgr/wakeup_fd_posix.h"
#include <errno.h> #include <errno.h>
@ -87,7 +90,8 @@ static int pipe_check_availability(void) {
return 1; return 1;
} }
const grpc_wakeup_fd_vtable pipe_wakeup_fd_vtable = { const grpc_wakeup_fd_vtable grpc_pipe_wakeup_fd_vtable = {
pipe_create, pipe_consume, pipe_wakeup, pipe_destroy, pipe_check_availability pipe_create, pipe_consume, pipe_wakeup, pipe_destroy, pipe_check_availability
}; };
#endif /* GPR_POSIX_WAKUP_FD */

@ -36,6 +36,6 @@
#include "src/core/iomgr/wakeup_fd_posix.h" #include "src/core/iomgr/wakeup_fd_posix.h"
extern grpc_wakeup_fd_vtable pipe_wakeup_fd_vtable; extern grpc_wakeup_fd_vtable grpc_pipe_wakeup_fd_vtable;
#endif /* __GRPC_INTERNAL_IOMGR_WAKEUP_FD_PIPE_H_ */ #endif /* __GRPC_INTERNAL_IOMGR_WAKEUP_FD_PIPE_H_ */

@ -31,6 +31,10 @@
* *
*/ */
#include <grpc/support/port_platform.h>
#ifdef GPR_POSIX_WAKEUP_FD
#include "src/core/iomgr/wakeup_fd_posix.h" #include "src/core/iomgr/wakeup_fd_posix.h"
#include "src/core/iomgr/wakeup_fd_pipe.h" #include "src/core/iomgr/wakeup_fd_pipe.h"
#include <stddef.h> #include <stddef.h>
@ -38,15 +42,15 @@
static const grpc_wakeup_fd_vtable *wakeup_fd_vtable = NULL; static const grpc_wakeup_fd_vtable *wakeup_fd_vtable = NULL;
void grpc_wakeup_fd_global_init(void) { void grpc_wakeup_fd_global_init(void) {
if (specialized_wakeup_fd_vtable.check_availability()) { if (grpc_specialized_wakeup_fd_vtable.check_availability()) {
wakeup_fd_vtable = &specialized_wakeup_fd_vtable; wakeup_fd_vtable = &grpc_specialized_wakeup_fd_vtable;
} else { } else {
wakeup_fd_vtable = &pipe_wakeup_fd_vtable; wakeup_fd_vtable = &grpc_pipe_wakeup_fd_vtable;
} }
} }
void grpc_wakeup_fd_global_init_force_fallback(void) { void grpc_wakeup_fd_global_init_force_fallback(void) {
wakeup_fd_vtable = &pipe_wakeup_fd_vtable; wakeup_fd_vtable = &grpc_pipe_wakeup_fd_vtable;
} }
void grpc_wakeup_fd_global_destroy(void) { void grpc_wakeup_fd_global_destroy(void) {
@ -68,3 +72,5 @@ void grpc_wakeup_fd_wakeup(grpc_wakeup_fd_info *fd_info) {
void grpc_wakeup_fd_destroy(grpc_wakeup_fd_info *fd_info) { void grpc_wakeup_fd_destroy(grpc_wakeup_fd_info *fd_info) {
wakeup_fd_vtable->destroy(fd_info); wakeup_fd_vtable->destroy(fd_info);
} }
#endif /* GPR_POSIX_WAKEUP_FD */

@ -62,29 +62,14 @@
#ifndef __GRPC_INTERNAL_IOMGR_WAKEUP_FD_POSIX_H_ #ifndef __GRPC_INTERNAL_IOMGR_WAKEUP_FD_POSIX_H_
#define __GRPC_INTERNAL_IOMGR_WAKEUP_FD_POSIX_H_ #define __GRPC_INTERNAL_IOMGR_WAKEUP_FD_POSIX_H_
typedef struct grpc_wakeup_fd_info grpc_wakeup_fd_info;
void grpc_wakeup_fd_global_init(void); void grpc_wakeup_fd_global_init(void);
void grpc_wakeup_fd_global_destroy(void); void grpc_wakeup_fd_global_destroy(void);
void grpc_wakeup_fd_create(grpc_wakeup_fd_info *fd_info);
void grpc_wakeup_fd_consume_wakeup(grpc_wakeup_fd_info *fd_info);
void grpc_wakeup_fd_wakeup(grpc_wakeup_fd_info *fd_info);
void grpc_wakeup_fd_destroy(grpc_wakeup_fd_info *fd_info);
#define GRPC_WAKEUP_FD_GET_READ_FD(fd_info) ((fd_info)->read_fd)
/* Force using the fallback implementation. This is intended for testing /* Force using the fallback implementation. This is intended for testing
* purposes only.*/ * purposes only.*/
void grpc_wakeup_fd_global_init_force_fallback(void); void grpc_wakeup_fd_global_init_force_fallback(void);
/* Private structures; don't access their fields directly outside of wakeup fd typedef struct grpc_wakeup_fd_info grpc_wakeup_fd_info;
* code. */
struct grpc_wakeup_fd_info {
int read_fd;
int write_fd;
};
typedef struct grpc_wakeup_fd_vtable { typedef struct grpc_wakeup_fd_vtable {
void (*create)(grpc_wakeup_fd_info *fd_info); void (*create)(grpc_wakeup_fd_info *fd_info);
@ -95,8 +80,20 @@ typedef struct grpc_wakeup_fd_vtable {
int (*check_availability)(void); int (*check_availability)(void);
} grpc_wakeup_fd_vtable; } grpc_wakeup_fd_vtable;
struct grpc_wakeup_fd_info {
int read_fd;
int write_fd;
};
#define GRPC_WAKEUP_FD_GET_READ_FD(fd_info) ((fd_info)->read_fd)
void grpc_wakeup_fd_create(grpc_wakeup_fd_info *fd_info);
void grpc_wakeup_fd_consume_wakeup(grpc_wakeup_fd_info *fd_info);
void grpc_wakeup_fd_wakeup(grpc_wakeup_fd_info *fd_info);
void grpc_wakeup_fd_destroy(grpc_wakeup_fd_info *fd_info);
/* Defined in some specialized implementation's .c file, or by /* Defined in some specialized implementation's .c file, or by
* wakeup_fd_nospecial.c if no such implementation exists. */ * wakeup_fd_nospecial.c if no such implementation exists. */
extern const grpc_wakeup_fd_vtable specialized_wakeup_fd_vtable; extern const grpc_wakeup_fd_vtable grpc_specialized_wakeup_fd_vtable;
#endif /* __GRPC_INTERNAL_IOMGR_WAKEUP_FD_POSIX_H_ */ #endif /* __GRPC_INTERNAL_IOMGR_WAKEUP_FD_POSIX_H_ */

@ -31,44 +31,17 @@
* *
*/ */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif /* _GNU_SOURCE */
#include <grpc/support/port_platform.h> #include <grpc/support/port_platform.h>
#ifdef GPR_CPU_LINUX #ifdef GPR_CPU_LINUX
#include "src/core/support/cpu.h" #include "src/core/support/cpu.h"
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#define GRPC_GNU_SOURCE
#endif
#ifndef __USE_GNU
#define __USE_GNU
#define GRPC_USE_GNU
#endif
#ifndef __USE_MISC
#define __USE_MISC
#define GRPC_USE_MISC
#endif
#include <sched.h> #include <sched.h>
#ifdef GRPC_GNU_SOURCE
#undef _GNU_SOURCE
#undef GRPC_GNU_SOURCE
#endif
#ifdef GRPC_USE_GNU
#undef __USE_GNU
#undef GRPC_USE_GNU
#endif
#ifdef GRPC_USE_MISC
#undef __USE_MISC
#undef GRPC_USE_MISC
#endif
#include <errno.h> #include <errno.h>
#include <unistd.h> #include <unistd.h>
#include <string.h> #include <string.h>

@ -64,7 +64,7 @@ void gpr_log(const char *file, int line, gpr_log_severity severity,
va_end(args); va_end(args);
if (ret < 0) { if (ret < 0) {
message = NULL; message = NULL;
} else if (ret <= sizeof(buf) - 1) { } else if ((size_t)ret <= sizeof(buf) - 1) {
message = buf; message = buf;
} else { } else {
message = allocated = gpr_malloc(ret + 1); message = allocated = gpr_malloc(ret + 1);

@ -62,17 +62,19 @@ int gpr_thd_new(gpr_thd_id *t, void (*thd_body)(void *arg), void *arg,
const gpr_thd_options *options) { const gpr_thd_options *options) {
int thread_started; int thread_started;
pthread_attr_t attr; pthread_attr_t attr;
pthread_t p;
struct thd_arg *a = gpr_malloc(sizeof(*a)); struct thd_arg *a = gpr_malloc(sizeof(*a));
a->body = thd_body; a->body = thd_body;
a->arg = arg; a->arg = arg;
GPR_ASSERT(pthread_attr_init(&attr) == 0); GPR_ASSERT(pthread_attr_init(&attr) == 0);
GPR_ASSERT(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) == 0); GPR_ASSERT(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) == 0);
thread_started = (pthread_create(t, &attr, &thread_body, a) == 0); thread_started = (pthread_create(&p, &attr, &thread_body, a) == 0);
GPR_ASSERT(pthread_attr_destroy(&attr) == 0); GPR_ASSERT(pthread_attr_destroy(&attr) == 0);
if (!thread_started) { if (!thread_started) {
gpr_free(a); gpr_free(a);
} }
*t = (gpr_thd_id)p;
return thread_started; return thread_started;
} }
@ -82,4 +84,8 @@ gpr_thd_options gpr_thd_options_default(void) {
return options; return options;
} }
gpr_thd_id gpr_thd_currentid(void) {
return (gpr_thd_id)pthread_self();
}
#endif /* GPR_POSIX_SYNC */ #endif /* GPR_POSIX_SYNC */

@ -58,16 +58,18 @@ static DWORD WINAPI thread_body(void *v) {
int gpr_thd_new(gpr_thd_id *t, void (*thd_body)(void *arg), void *arg, int gpr_thd_new(gpr_thd_id *t, void (*thd_body)(void *arg), void *arg,
const gpr_thd_options *options) { const gpr_thd_options *options) {
HANDLE handle; HANDLE handle;
DWORD thread_id;
struct thd_arg *a = gpr_malloc(sizeof(*a)); struct thd_arg *a = gpr_malloc(sizeof(*a));
a->body = thd_body; a->body = thd_body;
a->arg = arg; a->arg = arg;
*t = 0; *t = 0;
handle = CreateThread(NULL, 64 * 1024, thread_body, a, 0, NULL); handle = CreateThread(NULL, 64 * 1024, thread_body, a, 0, &thread_id);
if (handle == NULL) { if (handle == NULL) {
gpr_free(a); gpr_free(a);
} else { } else {
CloseHandle(handle); /* threads are "detached" */ CloseHandle(handle); /* threads are "detached" */
} }
*t = (gpr_thd_id)thread_id;
return handle != NULL; return handle != NULL;
} }
@ -77,4 +79,8 @@ gpr_thd_options gpr_thd_options_default(void) {
return options; return options;
} }
gpr_thd_id gpr_thd_currentid(void) {
return (gpr_thd_id)GetCurrentThreadId();
}
#endif /* GPR_WIN32 */ #endif /* GPR_WIN32 */

@ -348,7 +348,8 @@ void grpc_call_add_mdelem(grpc_call *call, grpc_mdelem *mdelem,
elem->filter->call_op(elem, NULL, &op); elem->filter->call_op(elem, NULL, &op);
} }
grpc_call_error grpc_call_add_metadata(grpc_call *call, grpc_metadata *metadata, grpc_call_error grpc_call_add_metadata_old(grpc_call *call,
grpc_metadata *metadata,
gpr_uint32 flags) { gpr_uint32 flags) {
grpc_mdelem *mdelem; grpc_mdelem *mdelem;
@ -455,9 +456,9 @@ static void call_started(void *user_data, grpc_op_error error) {
grpc_call_internal_unref(call); grpc_call_internal_unref(call);
} }
grpc_call_error grpc_call_invoke(grpc_call *call, grpc_completion_queue *cq, grpc_call_error grpc_call_invoke_old(grpc_call *call, grpc_completion_queue *cq,
void *metadata_read_tag, void *finished_tag, void *metadata_read_tag,
gpr_uint32 flags) { void *finished_tag, gpr_uint32 flags) {
grpc_call_element *elem; grpc_call_element *elem;
grpc_call_op op; grpc_call_op op;
@ -527,7 +528,7 @@ grpc_call_error grpc_call_invoke(grpc_call *call, grpc_completion_queue *cq,
return GRPC_CALL_OK; return GRPC_CALL_OK;
} }
grpc_call_error grpc_call_server_accept(grpc_call *call, grpc_call_error grpc_call_server_accept_old(grpc_call *call,
grpc_completion_queue *cq, grpc_completion_queue *cq,
void *finished_tag) { void *finished_tag) {
/* validate preconditions */ /* validate preconditions */
@ -563,7 +564,7 @@ grpc_call_error grpc_call_server_accept(grpc_call *call,
return GRPC_CALL_OK; return GRPC_CALL_OK;
} }
grpc_call_error grpc_call_server_end_initial_metadata(grpc_call *call, grpc_call_error grpc_call_server_end_initial_metadata_old(grpc_call *call,
gpr_uint32 flags) { gpr_uint32 flags) {
grpc_call_element *elem; grpc_call_element *elem;
grpc_call_op op; grpc_call_op op;
@ -634,7 +635,7 @@ static void request_more_data(grpc_call *call) {
elem->filter->call_op(elem, NULL, &op); elem->filter->call_op(elem, NULL, &op);
} }
grpc_call_error grpc_call_start_read(grpc_call *call, void *tag) { grpc_call_error grpc_call_start_read_old(grpc_call *call, void *tag) {
gpr_uint8 request_more = 0; gpr_uint8 request_more = 0;
switch (call->state) { switch (call->state) {
@ -677,9 +678,9 @@ grpc_call_error grpc_call_start_read(grpc_call *call, void *tag) {
return GRPC_CALL_OK; return GRPC_CALL_OK;
} }
grpc_call_error grpc_call_start_write(grpc_call *call, grpc_call_error grpc_call_start_write_old(grpc_call *call,
grpc_byte_buffer *byte_buffer, void *tag, grpc_byte_buffer *byte_buffer,
gpr_uint32 flags) { void *tag, gpr_uint32 flags) {
grpc_call_element *elem; grpc_call_element *elem;
grpc_call_op op; grpc_call_op op;
@ -732,7 +733,7 @@ grpc_call_error grpc_call_start_write(grpc_call *call,
return GRPC_CALL_OK; return GRPC_CALL_OK;
} }
grpc_call_error grpc_call_writes_done(grpc_call *call, void *tag) { grpc_call_error grpc_call_writes_done_old(grpc_call *call, void *tag) {
grpc_call_element *elem; grpc_call_element *elem;
grpc_call_op op; grpc_call_op op;
@ -780,9 +781,10 @@ grpc_call_error grpc_call_writes_done(grpc_call *call, void *tag) {
return GRPC_CALL_OK; return GRPC_CALL_OK;
} }
grpc_call_error grpc_call_start_write_status(grpc_call *call, grpc_call_error grpc_call_start_write_status_old(grpc_call *call,
grpc_status_code status, grpc_status_code status,
const char *details, void *tag) { const char *details,
void *tag) {
grpc_call_element *elem; grpc_call_element *elem;
grpc_call_op op; grpc_call_op op;

@ -74,8 +74,8 @@ grpc_channel *grpc_channel_create_from_filters(
static void do_nothing(void *ignored, grpc_op_error error) {} static void do_nothing(void *ignored, grpc_op_error error) {}
grpc_call *grpc_channel_create_call(grpc_channel *channel, const char *method, grpc_call *grpc_channel_create_call_old(grpc_channel *channel,
const char *host, const char *method, const char *host,
gpr_timespec absolute_deadline) { gpr_timespec absolute_deadline) {
grpc_call *call; grpc_call *call;
grpc_mdelem *path_mdelem; grpc_mdelem *path_mdelem;

@ -625,7 +625,8 @@ void grpc_server_add_listener(grpc_server *server, void *arg,
server->listeners = l; server->listeners = l;
} }
grpc_call_error grpc_server_request_call(grpc_server *server, void *tag_new) { grpc_call_error grpc_server_request_call_old(grpc_server *server,
void *tag_new) {
call_data *calld; call_data *calld;
grpc_cq_begin_op(server->cq, NULL, GRPC_SERVER_RPC_NEW); grpc_cq_begin_op(server->cq, NULL, GRPC_SERVER_RPC_NEW);

@ -37,6 +37,7 @@
#include <grpc/support/log.h> #include <grpc/support/log.h>
#include <grpc/support/sync.h> #include <grpc/support/sync.h>
#include <grpc/support/thd.h>
#include <grpc/support/useful.h> #include <grpc/support/useful.h>
#include "src/core/tsi/transport_security.h" #include "src/core/tsi/transport_security.h"
@ -103,11 +104,32 @@ typedef struct {
/* --- Library Initialization. ---*/ /* --- Library Initialization. ---*/
static gpr_once init_openssl_once = GPR_ONCE_INIT; static gpr_once init_openssl_once = GPR_ONCE_INIT;
static gpr_mu *openssl_mutexes = NULL;
static void openssl_locking_cb(int mode, int type, const char* file, int line) {
if (mode & CRYPTO_LOCK) {
gpr_mu_lock(&openssl_mutexes[type]);
} else {
gpr_mu_unlock(&openssl_mutexes[type]);
}
}
static unsigned long openssl_thread_id_cb(void) {
return (unsigned long)gpr_thd_currentid();
}
static void init_openssl(void) { static void init_openssl(void) {
int i;
SSL_library_init(); SSL_library_init();
SSL_load_error_strings(); SSL_load_error_strings();
OpenSSL_add_all_algorithms(); OpenSSL_add_all_algorithms();
openssl_mutexes = malloc(CRYPTO_num_locks() * sizeof(gpr_mu));
GPR_ASSERT(openssl_mutexes != NULL);
for (i = 0; i < CRYPTO_num_locks(); i++) {
gpr_mu_init(&openssl_mutexes[i]);
}
CRYPTO_set_locking_callback(openssl_locking_cb);
CRYPTO_set_id_callback(openssl_thread_id_cb);
} }
/* --- Ssl utils. ---*/ /* --- Ssl utils. ---*/

@ -99,7 +99,7 @@ Status Channel::StartBlockingRpc(const RpcMethod &method,
const google::protobuf::Message &request, const google::protobuf::Message &request,
google::protobuf::Message *result) { google::protobuf::Message *result) {
Status status; Status status;
grpc_call *call = grpc_channel_create_call( grpc_call *call = grpc_channel_create_call_old(
c_channel_, method.name(), target_.c_str(), context->RawDeadline()); c_channel_, method.name(), target_.c_str(), context->RawDeadline());
context->set_call(call); context->set_call(call);
grpc_event *ev; grpc_event *ev;
@ -114,7 +114,7 @@ Status Channel::StartBlockingRpc(const RpcMethod &method,
// add_metadata from context // add_metadata from context
// //
// invoke // invoke
GPR_ASSERT(grpc_call_invoke(call, cq, metadata_read_tag, finished_tag, GPR_ASSERT(grpc_call_invoke_old(call, cq, metadata_read_tag, finished_tag,
GRPC_WRITE_BUFFER_HINT) == GRPC_CALL_OK); GRPC_WRITE_BUFFER_HINT) == GRPC_CALL_OK);
// write request // write request
grpc_byte_buffer *write_buffer = nullptr; grpc_byte_buffer *write_buffer = nullptr;
@ -126,7 +126,7 @@ Status Channel::StartBlockingRpc(const RpcMethod &method,
GetFinalStatus(cq, finished_tag, nullptr); GetFinalStatus(cq, finished_tag, nullptr);
return status; return status;
} }
GPR_ASSERT(grpc_call_start_write(call, write_buffer, write_tag, GPR_ASSERT(grpc_call_start_write_old(call, write_buffer, write_tag,
GRPC_WRITE_BUFFER_HINT) == GRPC_CALL_OK); GRPC_WRITE_BUFFER_HINT) == GRPC_CALL_OK);
grpc_byte_buffer_destroy(write_buffer); grpc_byte_buffer_destroy(write_buffer);
ev = grpc_completion_queue_pluck(cq, write_tag, gpr_inf_future); ev = grpc_completion_queue_pluck(cq, write_tag, gpr_inf_future);
@ -138,7 +138,7 @@ Status Channel::StartBlockingRpc(const RpcMethod &method,
return status; return status;
} }
// writes done // writes done
GPR_ASSERT(grpc_call_writes_done(call, halfclose_tag) == GRPC_CALL_OK); GPR_ASSERT(grpc_call_writes_done_old(call, halfclose_tag) == GRPC_CALL_OK);
ev = grpc_completion_queue_pluck(cq, halfclose_tag, gpr_inf_future); ev = grpc_completion_queue_pluck(cq, halfclose_tag, gpr_inf_future);
grpc_event_finish(ev); grpc_event_finish(ev);
// start read metadata // start read metadata
@ -146,7 +146,7 @@ Status Channel::StartBlockingRpc(const RpcMethod &method,
ev = grpc_completion_queue_pluck(cq, metadata_read_tag, gpr_inf_future); ev = grpc_completion_queue_pluck(cq, metadata_read_tag, gpr_inf_future);
grpc_event_finish(ev); grpc_event_finish(ev);
// start read // start read
GPR_ASSERT(grpc_call_start_read(call, read_tag) == GRPC_CALL_OK); GPR_ASSERT(grpc_call_start_read_old(call, read_tag) == GRPC_CALL_OK);
ev = grpc_completion_queue_pluck(cq, read_tag, gpr_inf_future); ev = grpc_completion_queue_pluck(cq, read_tag, gpr_inf_future);
if (ev->data.read) { if (ev->data.read) {
if (!DeserializeProto(ev->data.read, result)) { if (!DeserializeProto(ev->data.read, result)) {
@ -167,7 +167,7 @@ StreamContextInterface *Channel::CreateStream(
const RpcMethod &method, ClientContext *context, const RpcMethod &method, ClientContext *context,
const google::protobuf::Message *request, const google::protobuf::Message *request,
google::protobuf::Message *result) { google::protobuf::Message *result) {
grpc_call *call = grpc_channel_create_call( grpc_call *call = grpc_channel_create_call_old(
c_channel_, method.name(), target_.c_str(), context->RawDeadline()); c_channel_, method.name(), target_.c_str(), context->RawDeadline());
context->set_call(call); context->set_call(call);
grpc_completion_queue *cq = grpc_completion_queue_create(); grpc_completion_queue *cq = grpc_completion_queue_create();

@ -72,7 +72,7 @@ void AsyncServer::RequestOneRpc() {
return; return;
} }
lock.unlock(); lock.unlock();
grpc_call_error err = grpc_server_request_call(server_, nullptr); grpc_call_error err = grpc_server_request_call_old(server_, nullptr);
GPR_ASSERT(err == GRPC_CALL_OK); GPR_ASSERT(err == GRPC_CALL_OK);
} }

@ -53,14 +53,15 @@ AsyncServerContext::AsyncServerContext(
AsyncServerContext::~AsyncServerContext() { grpc_call_destroy(call_); } AsyncServerContext::~AsyncServerContext() { grpc_call_destroy(call_); }
void AsyncServerContext::Accept(grpc_completion_queue *cq) { void AsyncServerContext::Accept(grpc_completion_queue *cq) {
GPR_ASSERT(grpc_call_server_accept(call_, cq, this) == GRPC_CALL_OK); GPR_ASSERT(grpc_call_server_accept_old(call_, cq, this) == GRPC_CALL_OK);
GPR_ASSERT(grpc_call_server_end_initial_metadata(call_, GRPC_WRITE_BUFFER_HINT) == GRPC_CALL_OK); GPR_ASSERT(grpc_call_server_end_initial_metadata_old(call_, GRPC_WRITE_BUFFER_HINT) ==
GRPC_CALL_OK);
} }
bool AsyncServerContext::StartRead(google::protobuf::Message *request) { bool AsyncServerContext::StartRead(google::protobuf::Message *request) {
GPR_ASSERT(request); GPR_ASSERT(request);
request_ = request; request_ = request;
grpc_call_error err = grpc_call_start_read(call_, this); grpc_call_error err = grpc_call_start_read_old(call_, this);
return err == GRPC_CALL_OK; return err == GRPC_CALL_OK;
} }
@ -70,13 +71,13 @@ bool AsyncServerContext::StartWrite(const google::protobuf::Message &response,
if (!SerializeProto(response, &buffer)) { if (!SerializeProto(response, &buffer)) {
return false; return false;
} }
grpc_call_error err = grpc_call_start_write(call_, buffer, this, flags); grpc_call_error err = grpc_call_start_write_old(call_, buffer, this, flags);
grpc_byte_buffer_destroy(buffer); grpc_byte_buffer_destroy(buffer);
return err == GRPC_CALL_OK; return err == GRPC_CALL_OK;
} }
bool AsyncServerContext::StartWriteStatus(const Status &status) { bool AsyncServerContext::StartWriteStatus(const Status &status) {
grpc_call_error err = grpc_call_start_write_status( grpc_call_error err = grpc_call_start_write_status_old(
call_, static_cast<grpc_status_code>(status.code()), call_, static_cast<grpc_status_code>(status.code()),
status.details().empty() ? nullptr status.details().empty() ? nullptr
: const_cast<char *>(status.details().c_str()), : const_cast<char *>(status.details().c_str()),

@ -111,7 +111,7 @@ void Server::Start() {
void Server::AllowOneRpc() { void Server::AllowOneRpc() {
GPR_ASSERT(started_); GPR_ASSERT(started_);
grpc_call_error err = grpc_server_request_call(server_, nullptr); grpc_call_error err = grpc_server_request_call_old(server_, nullptr);
GPR_ASSERT(err == GRPC_CALL_OK); GPR_ASSERT(err == GRPC_CALL_OK);
} }

@ -80,22 +80,22 @@ void StreamContext::Start(bool buffered) {
if (is_client_) { if (is_client_) {
// TODO(yangg) handle metadata send path // TODO(yangg) handle metadata send path
int flag = buffered ? GRPC_WRITE_BUFFER_HINT : 0; int flag = buffered ? GRPC_WRITE_BUFFER_HINT : 0;
grpc_call_error error = grpc_call_invoke( grpc_call_error error = grpc_call_invoke_old(
call(), cq(), client_metadata_read_tag(), finished_tag(), flag); call(), cq(), client_metadata_read_tag(), finished_tag(), flag);
GPR_ASSERT(GRPC_CALL_OK == error); GPR_ASSERT(GRPC_CALL_OK == error);
} else { } else {
// TODO(yangg) metadata needs to be added before accept // TODO(yangg) metadata needs to be added before accept
// TODO(yangg) correctly set flag to accept // TODO(yangg) correctly set flag to accept
GPR_ASSERT(grpc_call_server_accept(call(), cq(), finished_tag()) == GPR_ASSERT(grpc_call_server_accept_old(call(), cq(), finished_tag()) ==
GRPC_CALL_OK); GRPC_CALL_OK);
GPR_ASSERT(grpc_call_server_end_initial_metadata(call(), 0) == GPR_ASSERT(grpc_call_server_end_initial_metadata_old(call(), 0) ==
GRPC_CALL_OK); GRPC_CALL_OK);
} }
} }
bool StreamContext::Read(google::protobuf::Message *msg) { bool StreamContext::Read(google::protobuf::Message *msg) {
// TODO(yangg) check peer_halfclosed_ here for possible early return. // TODO(yangg) check peer_halfclosed_ here for possible early return.
grpc_call_error err = grpc_call_start_read(call(), read_tag()); grpc_call_error err = grpc_call_start_read_old(call(), read_tag());
GPR_ASSERT(err == GRPC_CALL_OK); GPR_ASSERT(err == GRPC_CALL_OK);
grpc_event *read_ev = grpc_event *read_ev =
grpc_completion_queue_pluck(cq(), read_tag(), gpr_inf_future); grpc_completion_queue_pluck(cq(), read_tag(), gpr_inf_future);
@ -129,7 +129,7 @@ bool StreamContext::Write(const google::protobuf::Message *msg, bool is_last) {
} }
int flag = is_last ? GRPC_WRITE_BUFFER_HINT : 0; int flag = is_last ? GRPC_WRITE_BUFFER_HINT : 0;
grpc_call_error err = grpc_call_error err =
grpc_call_start_write(call(), out_buf, write_tag(), flag); grpc_call_start_write_old(call(), out_buf, write_tag(), flag);
grpc_byte_buffer_destroy(out_buf); grpc_byte_buffer_destroy(out_buf);
GPR_ASSERT(err == GRPC_CALL_OK); GPR_ASSERT(err == GRPC_CALL_OK);
@ -140,7 +140,7 @@ bool StreamContext::Write(const google::protobuf::Message *msg, bool is_last) {
grpc_event_finish(ev); grpc_event_finish(ev);
} }
if (ret && is_last) { if (ret && is_last) {
grpc_call_error err = grpc_call_writes_done(call(), halfclose_tag()); grpc_call_error err = grpc_call_writes_done_old(call(), halfclose_tag());
GPR_ASSERT(err == GRPC_CALL_OK); GPR_ASSERT(err == GRPC_CALL_OK);
ev = grpc_completion_queue_pluck(cq(), halfclose_tag(), gpr_inf_future); ev = grpc_completion_queue_pluck(cq(), halfclose_tag(), gpr_inf_future);
GPR_ASSERT(ev->type == GRPC_FINISH_ACCEPTED); GPR_ASSERT(ev->type == GRPC_FINISH_ACCEPTED);

@ -0,0 +1,2 @@
*.userprefs
test-results

@ -0,0 +1,22 @@
gRPC C#
=======
A C# implementation of gRPC, Google's RPC library.
EXPERIMENTAL ONLY
-----------------
**This gRPC C# implementation is work-in-progress and is not expected to work yet.**
- The implementation is a wrapper around gRPC C core library
- Code only runs under mono currently, building gGRPC C core library under Windows
is in progress.
- It is very possible that some parts of the code will be heavily refactored or
completely rewritten.
CONTENTS
--------
- ext:
The extension library that wraps C API to be more digestible by C#.

@ -0,0 +1,113 @@
#include <grpc/grpc.h>
#include <grpc/support/log.h>
#include <grpc/support/slice.h>
#include <string.h>
grpc_byte_buffer *string_to_byte_buffer(const char *buffer, size_t len) {
gpr_slice slice = gpr_slice_from_copied_buffer(buffer, len);
grpc_byte_buffer *bb = grpc_byte_buffer_create(&slice, 1);
gpr_slice_unref(slice);
return bb;
}
void grpc_call_start_write_from_copied_buffer(grpc_call *call,
const char *buffer, size_t len,
void *tag, gpr_uint32 flags) {
grpc_byte_buffer *byte_buffer = string_to_byte_buffer(buffer, len);
GPR_ASSERT(grpc_call_start_write_old(call, byte_buffer, tag, flags) ==
GRPC_CALL_OK);
grpc_byte_buffer_destroy(byte_buffer);
}
grpc_completion_type grpc_event_type(const grpc_event *event) {
return event->type;
}
grpc_op_error grpc_event_write_accepted(const grpc_event *event) {
GPR_ASSERT(event->type == GRPC_WRITE_ACCEPTED);
return event->data.invoke_accepted;
}
grpc_op_error grpc_event_finish_accepted(const grpc_event *event) {
GPR_ASSERT(event->type == GRPC_FINISH_ACCEPTED);
return event->data.finish_accepted;
}
grpc_status_code grpc_event_finished_status(const grpc_event *event) {
GPR_ASSERT(event->type == GRPC_FINISHED);
return event->data.finished.status;
}
const char *grpc_event_finished_details(const grpc_event *event) {
GPR_ASSERT(event->type == GRPC_FINISHED);
return event->data.finished.details;
}
gpr_intptr grpc_event_read_length(const grpc_event *event) {
GPR_ASSERT(event->type == GRPC_READ);
if (!event->data.read) {
return -1;
}
return grpc_byte_buffer_length(event->data.read);
}
/*
* Copies data from read event to a buffer. Fatal error occurs if
* buffer is too small.
*/
void grpc_event_read_copy_to_buffer(const grpc_event *event, char *buffer,
size_t buffer_len) {
grpc_byte_buffer_reader *reader;
gpr_slice slice;
size_t offset = 0;
GPR_ASSERT(event->type == GRPC_READ);
reader = grpc_byte_buffer_reader_create(event->data.read);
GPR_ASSERT(event->data.read);
while (grpc_byte_buffer_reader_next(reader, &slice)) {
size_t len = GPR_SLICE_LENGTH(slice);
GPR_ASSERT(offset + len <= buffer_len);
memcpy(buffer + offset, GPR_SLICE_START_PTR(slice),
GPR_SLICE_LENGTH(slice));
offset += len;
gpr_slice_unref(slice);
}
grpc_byte_buffer_reader_destroy(reader);
}
grpc_call *grpc_event_call(const grpc_event *event) {
/* we only allow this for newly incoming server calls. */
GPR_ASSERT(event->type == GRPC_SERVER_RPC_NEW);
return event->call;
}
const char *grpc_event_server_rpc_new_method(const grpc_event *event) {
GPR_ASSERT(event->type == GRPC_SERVER_RPC_NEW);
return event->data.server_rpc_new.method;
}
grpc_completion_type grpc_completion_queue_next_with_callback(
grpc_completion_queue *cq) {
grpc_event *ev;
grpc_completion_type t;
void (*callback)(grpc_event *);
ev = grpc_completion_queue_next(cq, gpr_inf_future);
t = ev->type;
if (ev->tag) {
/* call the callback in ev->tag */
/* C forbids to cast object pointers to function pointers, so
* we cast to intptr first.
*/
callback = (void (*)(grpc_event *))(gpr_intptr)ev->tag;
(*callback)(ev);
}
grpc_event_finish(ev);
/* return completion type to allow some handling for events that have no
* tag - such as GRPC_QUEUE_SHUTDOWN
*/
return t;
}

@ -0,0 +1,2 @@
build
node_modules

@ -152,8 +152,8 @@ NAN_METHOD(Call::New) {
NanUtf8String method(args[1]); NanUtf8String method(args[1]);
double deadline = args[2]->NumberValue(); double deadline = args[2]->NumberValue();
grpc_channel *wrapped_channel = channel->GetWrappedChannel(); grpc_channel *wrapped_channel = channel->GetWrappedChannel();
grpc_call *wrapped_call = grpc_call *wrapped_call = grpc_channel_create_call_old(
grpc_channel_create_call(wrapped_channel, *method, channel->GetHost(), wrapped_channel, *method, channel->GetHost(),
MillisecondsToTimespec(deadline)); MillisecondsToTimespec(deadline));
call = new Call(wrapped_call); call = new Call(wrapped_call);
args.This()->SetHiddenValue(String::NewSymbol("channel_"), args.This()->SetHiddenValue(String::NewSymbol("channel_"),
@ -195,7 +195,7 @@ NAN_METHOD(Call::AddMetadata) {
if (Buffer::HasInstance(value)) { if (Buffer::HasInstance(value)) {
metadata.value = Buffer::Data(value); metadata.value = Buffer::Data(value);
metadata.value_length = Buffer::Length(value); metadata.value_length = Buffer::Length(value);
error = grpc_call_add_metadata(call->wrapped_call, &metadata, 0); error = grpc_call_add_metadata_old(call->wrapped_call, &metadata, 0);
} else if (value->IsString()) { } else if (value->IsString()) {
Handle<String> string_value = value->ToString(); Handle<String> string_value = value->ToString();
NanUtf8String utf8_value(string_value); NanUtf8String utf8_value(string_value);
@ -203,7 +203,7 @@ NAN_METHOD(Call::AddMetadata) {
metadata.value_length = string_value->Length(); metadata.value_length = string_value->Length();
gpr_log(GPR_DEBUG, "adding metadata: %s, %s, %d", metadata.key, gpr_log(GPR_DEBUG, "adding metadata: %s, %s, %d", metadata.key,
metadata.value, metadata.value_length); metadata.value, metadata.value_length);
error = grpc_call_add_metadata(call->wrapped_call, &metadata, 0); error = grpc_call_add_metadata_old(call->wrapped_call, &metadata, 0);
} else { } else {
return NanThrowTypeError( return NanThrowTypeError(
"addMetadata values must be strings or buffers"); "addMetadata values must be strings or buffers");
@ -232,7 +232,7 @@ NAN_METHOD(Call::Invoke) {
} }
Call *call = ObjectWrap::Unwrap<Call>(args.This()); Call *call = ObjectWrap::Unwrap<Call>(args.This());
unsigned int flags = args[3]->Uint32Value(); unsigned int flags = args[3]->Uint32Value();
grpc_call_error error = grpc_call_invoke( grpc_call_error error = grpc_call_invoke_old(
call->wrapped_call, CompletionQueueAsyncWorker::GetQueue(), call->wrapped_call, CompletionQueueAsyncWorker::GetQueue(),
CreateTag(args[0], args.This()), CreateTag(args[1], args.This()), flags); CreateTag(args[0], args.This()), CreateTag(args[1], args.This()), flags);
if (error == GRPC_CALL_OK) { if (error == GRPC_CALL_OK) {
@ -253,7 +253,7 @@ NAN_METHOD(Call::ServerAccept) {
return NanThrowTypeError("accept's first argument must be a function"); return NanThrowTypeError("accept's first argument must be a function");
} }
Call *call = ObjectWrap::Unwrap<Call>(args.This()); Call *call = ObjectWrap::Unwrap<Call>(args.This());
grpc_call_error error = grpc_call_server_accept( grpc_call_error error = grpc_call_server_accept_old(
call->wrapped_call, CompletionQueueAsyncWorker::GetQueue(), call->wrapped_call, CompletionQueueAsyncWorker::GetQueue(),
CreateTag(args[0], args.This())); CreateTag(args[0], args.This()));
if (error == GRPC_CALL_OK) { if (error == GRPC_CALL_OK) {
@ -277,7 +277,7 @@ NAN_METHOD(Call::ServerEndInitialMetadata) {
Call *call = ObjectWrap::Unwrap<Call>(args.This()); Call *call = ObjectWrap::Unwrap<Call>(args.This());
unsigned int flags = args[1]->Uint32Value(); unsigned int flags = args[1]->Uint32Value();
grpc_call_error error = grpc_call_error error =
grpc_call_server_end_initial_metadata(call->wrapped_call, flags); grpc_call_server_end_initial_metadata_old(call->wrapped_call, flags);
if (error != GRPC_CALL_OK) { if (error != GRPC_CALL_OK) {
return NanThrowError("serverEndInitialMetadata failed", error); return NanThrowError("serverEndInitialMetadata failed", error);
} }
@ -315,7 +315,7 @@ NAN_METHOD(Call::StartWrite) {
Call *call = ObjectWrap::Unwrap<Call>(args.This()); Call *call = ObjectWrap::Unwrap<Call>(args.This());
grpc_byte_buffer *buffer = BufferToByteBuffer(args[0]); grpc_byte_buffer *buffer = BufferToByteBuffer(args[0]);
unsigned int flags = args[2]->Uint32Value(); unsigned int flags = args[2]->Uint32Value();
grpc_call_error error = grpc_call_start_write( grpc_call_error error = grpc_call_start_write_old(
call->wrapped_call, buffer, CreateTag(args[1], args.This()), flags); call->wrapped_call, buffer, CreateTag(args[1], args.This()), flags);
if (error == GRPC_CALL_OK) { if (error == GRPC_CALL_OK) {
CompletionQueueAsyncWorker::Next(); CompletionQueueAsyncWorker::Next();
@ -345,7 +345,7 @@ NAN_METHOD(Call::StartWriteStatus) {
} }
Call *call = ObjectWrap::Unwrap<Call>(args.This()); Call *call = ObjectWrap::Unwrap<Call>(args.This());
NanUtf8String details(args[1]); NanUtf8String details(args[1]);
grpc_call_error error = grpc_call_start_write_status( grpc_call_error error = grpc_call_start_write_status_old(
call->wrapped_call, (grpc_status_code)args[0]->Uint32Value(), *details, call->wrapped_call, (grpc_status_code)args[0]->Uint32Value(), *details,
CreateTag(args[2], args.This())); CreateTag(args[2], args.This()));
if (error == GRPC_CALL_OK) { if (error == GRPC_CALL_OK) {
@ -365,7 +365,7 @@ NAN_METHOD(Call::WritesDone) {
return NanThrowTypeError("writesDone's first argument must be a function"); return NanThrowTypeError("writesDone's first argument must be a function");
} }
Call *call = ObjectWrap::Unwrap<Call>(args.This()); Call *call = ObjectWrap::Unwrap<Call>(args.This());
grpc_call_error error = grpc_call_writes_done( grpc_call_error error = grpc_call_writes_done_old(
call->wrapped_call, CreateTag(args[0], args.This())); call->wrapped_call, CreateTag(args[0], args.This()));
if (error == GRPC_CALL_OK) { if (error == GRPC_CALL_OK) {
CompletionQueueAsyncWorker::Next(); CompletionQueueAsyncWorker::Next();
@ -384,8 +384,8 @@ NAN_METHOD(Call::StartRead) {
return NanThrowTypeError("startRead's first argument must be a function"); return NanThrowTypeError("startRead's first argument must be a function");
} }
Call *call = ObjectWrap::Unwrap<Call>(args.This()); Call *call = ObjectWrap::Unwrap<Call>(args.This());
grpc_call_error error = grpc_call_error error = grpc_call_start_read_old(
grpc_call_start_read(call->wrapped_call, CreateTag(args[0], args.This())); call->wrapped_call, CreateTag(args[0], args.This()));
if (error == GRPC_CALL_OK) { if (error == GRPC_CALL_OK) {
CompletionQueueAsyncWorker::Next(); CompletionQueueAsyncWorker::Next();
} else { } else {

@ -175,7 +175,7 @@ NAN_METHOD(Server::RequestCall) {
return NanThrowTypeError("requestCall can only be called on a Server"); return NanThrowTypeError("requestCall can only be called on a Server");
} }
Server *server = ObjectWrap::Unwrap<Server>(args.This()); Server *server = ObjectWrap::Unwrap<Server>(args.This());
grpc_call_error error = grpc_server_request_call( grpc_call_error error = grpc_server_request_call_old(
server->wrapped_server, CreateTag(args[0], NanNull())); server->wrapped_server, CreateTag(args[0], NanNull()));
if (error == GRPC_CALL_OK) { if (error == GRPC_CALL_OK) {
CompletionQueueAsyncWorker::Next(); CompletionQueueAsyncWorker::Next();

@ -7,31 +7,25 @@ Directory structure is as generated by the PHP utility
## ENVIRONMENT ## ENVIRONMENT
To build a PHP environment that works with this extension, download and extract Install `php5` and `php5-dev`.
PHP 5.5 (5.6 may also work), configure it, and install it:
```bash To run the tests, additionally install `php5-readline` and `phpunit`.
apt-get install libxml2 libxml2-dev
curl http://php.net/get/php-5.5.16.tar.gz Alternatively, build and install PHP 5.5 or later from source with standard
tar -xf php-5.5.16.tar.gz configuration options.
cd php-5.5.16
./configure --with-zlib=/usr --with-libxml-dir=ext/libxml --with-openssl=/usr/local/ssl
make
make install
```
To also download and install the patched protoc and PHP code generator: To also download and install protoc and the PHP code generator.
```bash ```bash
apt-get install -y procps apt-get install -y procps
curl -sSL https://get.rvm.io | sudo bash -s stable --ruby curl -sSL https://get.rvm.io | sudo bash -s stable --ruby
git clone sso://team/one-platform-grpc-team/protobuf git clone git@github.com:google/protobuf.git
cd protobuf cd protobuf
./configure ./configure
make make
make install make install
git clone sso://team/one-platform-grpc-team/grpc-php-protobuf-php git clone git@github.com:murgatroid99/Protobuf-PHP.git
cd grpc-php-protobuf-php cd Protobuf-PHP
rake pear:package version=1.0 rake pear:package version=1.0
pear install Protobuf-1.0.tgz pear install Protobuf-1.0.tgz
``` ```
@ -52,5 +46,4 @@ This repo now has PHPUnit tests, which can by run by executing
There is also a generated code test (`./bin/run_gen_code_test.sh`), which tests There is also a generated code test (`./bin/run_gen_code_test.sh`), which tests
the stub `./tests/generated_code/math.php` against a running localhost server the stub `./tests/generated_code/math.php` against a running localhost server
serving the math service. That stub is generated from serving the math service. That stub is generated from
`./tests/generated_code/math.proto` with the head of the repo `./tests/generated_code/math.proto`.
`sso://team/one-platform-grpc-team/grpc-php-protobuf-php`.

@ -85,17 +85,7 @@ zval *grpc_call_create_metadata_array(int count, grpc_metadata *elements) {
memcpy(str_val, elem->value, elem->value_length); memcpy(str_val, elem->value, elem->value_length);
if (zend_hash_find(array_hash, str_key, key_len, (void **)data) == if (zend_hash_find(array_hash, str_key, key_len, (void **)data) ==
SUCCESS) { SUCCESS) {
switch (Z_TYPE_P(*data)) { if (Z_TYPE_P(*data) != IS_ARRAY) {
case IS_STRING:
MAKE_STD_ZVAL(inner_array);
array_init(inner_array);
add_next_index_zval(inner_array, *data);
add_assoc_zval(array, str_key, inner_array);
break;
case IS_ARRAY:
inner_array = *data;
break;
default:
zend_throw_exception(zend_exception_get_default(), zend_throw_exception(zend_exception_get_default(),
"Metadata hash somehow contains wrong types.", "Metadata hash somehow contains wrong types.",
1 TSRMLS_CC); 1 TSRMLS_CC);
@ -103,55 +93,17 @@ zval *grpc_call_create_metadata_array(int count, grpc_metadata *elements) {
efree(str_val); efree(str_val);
return NULL; return NULL;
} }
add_next_index_stringl(inner_array, str_val, elem->value_length, false); add_next_index_stringl(*data, str_val, elem->value_length, false);
} else { } else {
add_assoc_stringl(array, str_key, str_val, elem->value_length, false); MAKE_STD_ZVAL(inner_array);
array_init(inner_array);
add_next_index_stringl(inner_array, str_val, elem->value_length, false);
add_assoc_zval(array, str_key, inner_array);
} }
} }
return array; return array;
} }
int php_grpc_call_add_metadata_array_walk(void *elem TSRMLS_DC, int num_args,
va_list args,
zend_hash_key *hash_key) {
grpc_call_error error_code;
zval **data = (zval **)elem;
grpc_metadata metadata;
grpc_call *call = va_arg(args, grpc_call *);
gpr_uint32 flags = va_arg(args, gpr_uint32);
const char *key;
HashTable *inner_hash;
/* We assume that either two args were passed, and we are in the recursive
case (and the second argument is the key), or one arg was passed and
hash_key is the string key. */
if (num_args > 2) {
key = va_arg(args, const char *);
} else {
/* TODO(mlumish): If possible, check that hash_key is a string */
key = hash_key->arKey;
}
switch (Z_TYPE_P(*data)) {
case IS_STRING:
metadata.key = (char *)key;
metadata.value = Z_STRVAL_P(*data);
metadata.value_length = Z_STRLEN_P(*data);
error_code = grpc_call_add_metadata(call, &metadata, 0u);
MAYBE_THROW_CALL_ERROR(add_metadata, error_code);
break;
case IS_ARRAY:
inner_hash = Z_ARRVAL_P(*data);
zend_hash_apply_with_arguments(inner_hash TSRMLS_CC,
php_grpc_call_add_metadata_array_walk, 3,
call, flags, key);
break;
default:
zend_throw_exception(zend_exception_get_default(),
"Metadata hash somehow contains wrong types.",
1 TSRMLS_CC);
}
return ZEND_HASH_APPLY_KEEP;
}
/** /**
* Constructs a new instance of the Call class. * Constructs a new instance of the Call class.
* @param Channel $channel The channel to associate the call with. Must not be * @param Channel $channel The channel to associate the call with. Must not be
@ -188,8 +140,8 @@ PHP_METHOD(Call, __construct) {
wrapped_grpc_timeval *deadline = wrapped_grpc_timeval *deadline =
(wrapped_grpc_timeval *)zend_object_store_get_object( (wrapped_grpc_timeval *)zend_object_store_get_object(
deadline_obj TSRMLS_CC); deadline_obj TSRMLS_CC);
call->wrapped = grpc_channel_create_call(channel->wrapped, method, call->wrapped = grpc_channel_create_call_old(
channel->target, deadline->wrapped); channel->wrapped, method, channel->target, deadline->wrapped);
} }
/** /**
@ -204,8 +156,18 @@ PHP_METHOD(Call, __construct) {
PHP_METHOD(Call, add_metadata) { PHP_METHOD(Call, add_metadata) {
wrapped_grpc_call *call = wrapped_grpc_call *call =
(wrapped_grpc_call *)zend_object_store_get_object(getThis() TSRMLS_CC); (wrapped_grpc_call *)zend_object_store_get_object(getThis() TSRMLS_CC);
grpc_metadata metadata;
grpc_call_error error_code;
zval *array; zval *array;
zval **inner_array;
zval **value;
HashTable *array_hash; HashTable *array_hash;
HashPosition array_pointer;
HashTable *inner_array_hash;
HashPosition inner_array_pointer;
char *key;
uint key_len;
ulong index;
long flags = 0; long flags = 0;
/* "a|l" == 1 array, 1 optional long */ /* "a|l" == 1 array, 1 optional long */
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &flags) == if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &flags) ==
@ -216,9 +178,41 @@ PHP_METHOD(Call, add_metadata) {
return; return;
} }
array_hash = Z_ARRVAL_P(array); array_hash = Z_ARRVAL_P(array);
zend_hash_apply_with_arguments(array_hash TSRMLS_CC, for (zend_hash_internal_pointer_reset_ex(array_hash, &array_pointer);
php_grpc_call_add_metadata_array_walk, 2, zend_hash_get_current_data_ex(array_hash, (void**)&inner_array,
call->wrapped, (gpr_uint32)flags); &array_pointer) == SUCCESS;
zend_hash_move_forward_ex(array_hash, &array_pointer)) {
if (zend_hash_get_current_key_ex(array_hash, &key, &key_len, &index, 0,
&array_pointer) != HASH_KEY_IS_STRING) {
zend_throw_exception(spl_ce_InvalidArgumentException,
"metadata keys must be strings", 1 TSRMLS_CC);
return;
}
if (Z_TYPE_P(*inner_array) != IS_ARRAY) {
zend_throw_exception(spl_ce_InvalidArgumentException,
"metadata values must be arrays",
1 TSRMLS_CC);
return;
}
inner_array_hash = Z_ARRVAL_P(*inner_array);
for (zend_hash_internal_pointer_reset_ex(inner_array_hash,
&inner_array_pointer);
zend_hash_get_current_data_ex(inner_array_hash, (void**)&value,
&inner_array_pointer) == SUCCESS;
zend_hash_move_forward_ex(inner_array_hash, &inner_array_pointer)) {
if (Z_TYPE_P(*value) != IS_STRING) {
zend_throw_exception(spl_ce_InvalidArgumentException,
"metadata values must be arrays of strings",
1 TSRMLS_CC);
return;
}
metadata.key = key;
metadata.value = Z_STRVAL_P(*value);
metadata.value_length = Z_STRLEN_P(*value);
error_code = grpc_call_add_metadata_old(call->wrapped, &metadata, 0u);
MAYBE_THROW_CALL_ERROR(add_metadata, error_code);
}
}
} }
/** /**
@ -252,7 +246,7 @@ PHP_METHOD(Call, invoke) {
wrapped_grpc_completion_queue *queue = wrapped_grpc_completion_queue *queue =
(wrapped_grpc_completion_queue *)zend_object_store_get_object( (wrapped_grpc_completion_queue *)zend_object_store_get_object(
queue_obj TSRMLS_CC); queue_obj TSRMLS_CC);
error_code = grpc_call_invoke(call->wrapped, queue->wrapped, (void *)tag1, error_code = grpc_call_invoke_old(call->wrapped, queue->wrapped, (void *)tag1,
(void *)tag2, (gpr_uint32)flags); (void *)tag2, (gpr_uint32)flags);
MAYBE_THROW_CALL_ERROR(invoke, error_code); MAYBE_THROW_CALL_ERROR(invoke, error_code);
} }
@ -287,7 +281,7 @@ PHP_METHOD(Call, server_accept) {
(wrapped_grpc_completion_queue *)zend_object_store_get_object( (wrapped_grpc_completion_queue *)zend_object_store_get_object(
queue_obj TSRMLS_CC); queue_obj TSRMLS_CC);
error_code = error_code =
grpc_call_server_accept(call->wrapped, queue->wrapped, (void *)tag); grpc_call_server_accept_old(call->wrapped, queue->wrapped, (void *)tag);
MAYBE_THROW_CALL_ERROR(server_accept, error_code); MAYBE_THROW_CALL_ERROR(server_accept, error_code);
} }
@ -303,7 +297,7 @@ PHP_METHOD(Call, server_end_initial_metadata) {
} }
wrapped_grpc_call *call = wrapped_grpc_call *call =
(wrapped_grpc_call *)zend_object_store_get_object(getThis() TSRMLS_CC); (wrapped_grpc_call *)zend_object_store_get_object(getThis() TSRMLS_CC);
error_code = grpc_call_server_end_initial_metadata(call->wrapped, flags); error_code = grpc_call_server_end_initial_metadata_old(call->wrapped, flags);
MAYBE_THROW_CALL_ERROR(server_end_initial_metadata, error_code); MAYBE_THROW_CALL_ERROR(server_end_initial_metadata, error_code);
} }
@ -342,9 +336,9 @@ PHP_METHOD(Call, start_write) {
1 TSRMLS_CC); 1 TSRMLS_CC);
return; return;
} }
error_code = grpc_call_start_write(call->wrapped, error_code = grpc_call_start_write_old(
string_to_byte_buffer(buffer, buffer_len), call->wrapped, string_to_byte_buffer(buffer, buffer_len), (void *)tag,
(void *)tag, (gpr_uint32)flags); (gpr_uint32)flags);
MAYBE_THROW_CALL_ERROR(start_write, error_code); MAYBE_THROW_CALL_ERROR(start_write, error_code);
} }
@ -372,8 +366,8 @@ PHP_METHOD(Call, start_write_status) {
"start_write_status expects a long, a string, and a long", 1 TSRMLS_CC); "start_write_status expects a long, a string, and a long", 1 TSRMLS_CC);
return; return;
} }
error_code = error_code = grpc_call_start_write_status_old(call->wrapped,
grpc_call_start_write_status(call->wrapped, (grpc_status_code)status_code, (grpc_status_code)status_code,
status_details, (void *)tag); status_details, (void *)tag);
MAYBE_THROW_CALL_ERROR(start_write_status, error_code); MAYBE_THROW_CALL_ERROR(start_write_status, error_code);
} }
@ -393,7 +387,7 @@ PHP_METHOD(Call, writes_done) {
"writes_done expects a long", 1 TSRMLS_CC); "writes_done expects a long", 1 TSRMLS_CC);
return; return;
} }
error_code = grpc_call_writes_done(call->wrapped, (void *)tag); error_code = grpc_call_writes_done_old(call->wrapped, (void *)tag);
MAYBE_THROW_CALL_ERROR(writes_done, error_code); MAYBE_THROW_CALL_ERROR(writes_done, error_code);
} }
@ -414,7 +408,7 @@ PHP_METHOD(Call, start_read) {
"start_read expects a long", 1 TSRMLS_CC); "start_read expects a long", 1 TSRMLS_CC);
return; return;
} }
error_code = grpc_call_start_read(call->wrapped, (void *)tag); error_code = grpc_call_start_read_old(call->wrapped, (void *)tag);
MAYBE_THROW_CALL_ERROR(start_read, error_code); MAYBE_THROW_CALL_ERROR(start_read, error_code);
} }

@ -19,6 +19,7 @@
zend_throw_exception(spl_ce_LogicException, \ zend_throw_exception(spl_ce_LogicException, \
#func_name " was called incorrectly", \ #func_name " was called incorrectly", \
(long)error_code TSRMLS_CC); \ (long)error_code TSRMLS_CC); \
return; \
} \ } \
} while (0) } while (0)

@ -125,7 +125,7 @@ PHP_METHOD(Server, request_call) {
"request_call expects a long", 1 TSRMLS_CC); "request_call expects a long", 1 TSRMLS_CC);
return; return;
} }
error_code = grpc_server_request_call(server->wrapped, (void *)tag_new); error_code = grpc_server_request_call_old(server->wrapped, (void *)tag_new);
MAYBE_THROW_CALL_ERROR(request_call, error_code); MAYBE_THROW_CALL_ERROR(request_call, error_code);
} }
@ -146,7 +146,7 @@ PHP_METHOD(Server, add_http2_port) {
"add_http2_port expects a string", 1 TSRMLS_CC); "add_http2_port expects a string", 1 TSRMLS_CC);
return; return;
} }
RETURN_BOOL(grpc_server_add_http2_port(server->wrapped, addr)); RETURN_LONG(grpc_server_add_http2_port(server->wrapped, addr));
} }
PHP_METHOD(Server, add_secure_http2_port) { PHP_METHOD(Server, add_secure_http2_port) {
@ -161,7 +161,7 @@ PHP_METHOD(Server, add_secure_http2_port) {
"add_http2_port expects a string", 1 TSRMLS_CC); "add_http2_port expects a string", 1 TSRMLS_CC);
return; return;
} }
RETURN_BOOL(grpc_server_add_secure_http2_port(server->wrapped, addr)); RETURN_LONG(grpc_server_add_secure_http2_port(server->wrapped, addr));
} }
/** /**

@ -44,7 +44,7 @@ abstract class AbstractSurfaceActiveCall {
protected function _read() { protected function _read() {
$response = $this->active_call->read(); $response = $this->active_call->read();
if ($response == null) { if ($response === null) {
return null; return null;
} }
return call_user_func($this->deserialize, $response); return call_user_func($this->deserialize, $response);

@ -66,12 +66,7 @@ class ActiveCall {
* @param ByteBuffer $data The data to write * @param ByteBuffer $data The data to write
*/ */
public function write($data) { public function write($data) {
if($this->call->start_write($data, $this->call->start_write($data, WRITE_ACCEPTED, $this->flags);
WRITE_ACCEPTED,
$this->flags) != OP_OK) {
// TODO(mlumish): more useful error
throw new \Exception("Cannot call write after writesDone");
}
$this->completion_queue->pluck(WRITE_ACCEPTED, Timeval::inf_future()); $this->completion_queue->pluck(WRITE_ACCEPTED, Timeval::inf_future());
} }

@ -31,7 +31,7 @@ class ServerStreamingSurfaceActiveCall extends AbstractSurfaceActiveCall {
* @return An iterator of response values * @return An iterator of response values
*/ */
public function responses() { public function responses() {
while(($response = $this->_read()) != null) { while(($response = $this->_read()) !== null) {
yield $response; yield $response;
} }
} }

@ -17,9 +17,9 @@ class GeneratedCodeTest extends PHPUnit_Framework_TestCase {
$div_arg->setDividend(7); $div_arg->setDividend(7);
$div_arg->setDivisor(4); $div_arg->setDivisor(4);
list($response, $status) = self::$client->Div($div_arg)->wait(); list($response, $status) = self::$client->Div($div_arg)->wait();
$this->assertEquals(1, $response->getQuotient()); $this->assertSame(1, $response->getQuotient());
$this->assertEquals(3, $response->getRemainder()); $this->assertSame(3, $response->getRemainder());
$this->assertEquals(\Grpc\STATUS_OK, $status->code); $this->assertSame(\Grpc\STATUS_OK, $status->code);
} }
public function testServerStreaming() { public function testServerStreaming() {
@ -31,9 +31,9 @@ class GeneratedCodeTest extends PHPUnit_Framework_TestCase {
return $num->getNum(); return $num->getNum();
}; };
$values = array_map($extract_num, $result_array); $values = array_map($extract_num, $result_array);
$this->assertEquals([1, 1, 2, 3, 5, 8, 13], $values); $this->assertSame([1, 1, 2, 3, 5, 8, 13], $values);
$status = $call->getStatus(); $status = $call->getStatus();
$this->assertEquals(\Grpc\STATUS_OK, $status->code); $this->assertSame(\Grpc\STATUS_OK, $status->code);
} }
public function testClientStreaming() { public function testClientStreaming() {
@ -46,8 +46,8 @@ class GeneratedCodeTest extends PHPUnit_Framework_TestCase {
}; };
$call = self::$client->Sum($num_iter()); $call = self::$client->Sum($num_iter());
list($response, $status) = $call->wait(); list($response, $status) = $call->wait();
$this->assertEquals(21, $response->getNum()); $this->assertSame(21, $response->getNum());
$this->assertEquals(\Grpc\STATUS_OK, $status->code); $this->assertSame(\Grpc\STATUS_OK, $status->code);
} }
public function testBidiStreaming() { public function testBidiStreaming() {
@ -58,11 +58,11 @@ class GeneratedCodeTest extends PHPUnit_Framework_TestCase {
$div_arg->setDivisor(2); $div_arg->setDivisor(2);
$call->write($div_arg); $call->write($div_arg);
$response = $call->read(); $response = $call->read();
$this->assertEquals($i, $response->getQuotient()); $this->assertSame($i, $response->getQuotient());
$this->assertEquals(1, $response->getRemainder()); $this->assertSame(1, $response->getRemainder());
} }
$call->writesDone(); $call->writesDone();
$status = $call->getStatus(); $status = $call->getStatus();
$this->assertEquals(\Grpc\STATUS_OK, $status->code); $this->assertSame(\Grpc\STATUS_OK, $status->code);
} }
} }

@ -26,8 +26,8 @@ function hardAssert($value, $error_message) {
*/ */
function emptyUnary($stub) { function emptyUnary($stub) {
list($result, $status) = $stub->EmptyCall(new grpc\testing\EmptyMessage())->wait(); list($result, $status) = $stub->EmptyCall(new grpc\testing\EmptyMessage())->wait();
hardAssert($status->code == Grpc\STATUS_OK, 'Call did not complete successfully'); hardAssert($status->code === Grpc\STATUS_OK, 'Call did not complete successfully');
hardAssert($result != null, 'Call completed with a null response'); hardAssert($result !== null, 'Call completed with a null response');
} }
/** /**
@ -49,14 +49,14 @@ function largeUnary($stub) {
$request->setPayload($payload); $request->setPayload($payload);
list($result, $status) = $stub->UnaryCall($request)->wait(); list($result, $status) = $stub->UnaryCall($request)->wait();
hardAssert($status->code == Grpc\STATUS_OK, 'Call did not complete successfully'); hardAssert($status->code === Grpc\STATUS_OK, 'Call did not complete successfully');
hardAssert($result != null, 'Call returned a null response'); hardAssert($result !== null, 'Call returned a null response');
$payload = $result->getPayload(); $payload = $result->getPayload();
hardAssert($payload->getType() == grpc\testing\PayloadType::COMPRESSABLE, hardAssert($payload->getType() === grpc\testing\PayloadType::COMPRESSABLE,
'Payload had the wrong type'); 'Payload had the wrong type');
hardAssert(strlen($payload->getBody()) == $response_len, hardAssert(strlen($payload->getBody()) === $response_len,
'Payload had the wrong length'); 'Payload had the wrong length');
hardAssert($payload->getBody() == str_repeat("\0", $response_len), hardAssert($payload->getBody() === str_repeat("\0", $response_len),
'Payload had the wrong content'); 'Payload had the wrong content');
} }
@ -78,8 +78,8 @@ function clientStreaming($stub) {
}, $request_lengths); }, $request_lengths);
list($result, $status) = $stub->StreamingInputCall($requests)->wait(); list($result, $status) = $stub->StreamingInputCall($requests)->wait();
hardAssert($status->code == Grpc\STATUS_OK, 'Call did not complete successfully'); hardAssert($status->code === Grpc\STATUS_OK, 'Call did not complete successfully');
hardAssert($result->getAggregatedPayloadSize() == 74922, hardAssert($result->getAggregatedPayloadSize() === 74922,
'aggregated_payload_size was incorrect'); 'aggregated_payload_size was incorrect');
} }
@ -100,15 +100,15 @@ function serverStreaming($stub) {
} }
$call = $stub->StreamingOutputCall($request); $call = $stub->StreamingOutputCall($request);
hardAssert($call->getStatus()->code == Grpc\STATUS_OK, hardAssert($call->getStatus()->code === Grpc\STATUS_OK,
'Call did not complete successfully'); 'Call did not complete successfully');
$i = 0; $i = 0;
foreach($call->responses() as $value) { foreach($call->responses() as $value) {
hardAssert($i < 4, 'Too many responses'); hardAssert($i < 4, 'Too many responses');
$payload = $value->getPayload(); $payload = $value->getPayload();
hardAssert($payload->getType() == grpc\testing\PayloadType::COMPRESSABLE, hardAssert($payload->getType() === grpc\testing\PayloadType::COMPRESSABLE,
'Payload ' . $i . ' had the wrong type'); 'Payload ' . $i . ' had the wrong type');
hardAssert(strlen($payload->getBody()) == $sizes[$i], hardAssert(strlen($payload->getBody()) === $sizes[$i],
'Response ' . $i . ' had the wrong length'); 'Response ' . $i . ' had the wrong length');
} }
} }
@ -136,19 +136,38 @@ function pingPong($stub) {
$call->write($request); $call->write($request);
$response = $call->read(); $response = $call->read();
hardAssert($response != null, 'Server returned too few responses'); hardAssert($response !== null, 'Server returned too few responses');
$payload = $response->getPayload(); $payload = $response->getPayload();
hardAssert($payload->getType() == grpc\testing\PayloadType::COMPRESSABLE, hardAssert($payload->getType() === grpc\testing\PayloadType::COMPRESSABLE,
'Payload ' . $i . ' had the wrong type'); 'Payload ' . $i . ' had the wrong type');
hardAssert(strlen($payload->getBody()) == $response_lengths[$i], hardAssert(strlen($payload->getBody()) === $response_lengths[$i],
'Payload ' . $i . ' had the wrong length'); 'Payload ' . $i . ' had the wrong length');
} }
$call->writesDone(); $call->writesDone();
hardAssert($call->read() == null, 'Server returned too many responses'); hardAssert($call->read() === null, 'Server returned too many responses');
hardAssert($call->getStatus()->code == Grpc\STATUS_OK, hardAssert($call->getStatus()->code === Grpc\STATUS_OK,
'Call did not complete successfully'); 'Call did not complete successfully');
} }
function cancelAfterFirstResponse($stub) {
$call = $stub->FullDuplexCall();
$request = new grpc\testing\StreamingOutputCallRequest();
$request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE);
$response_parameters = new grpc\testing\ResponseParameters();
$response_parameters->setSize(31415);
$request->addResponseParameters($response_parameters);
$payload = new grpc\testing\Payload();
$payload->setBody(str_repeat("\0", 27182));
$request->setPayload($payload);
$call->write($request);
$response = $call->read();
$call->cancel();
hardAssert($call->getStatus()->code === Grpc\STATUS_CANCELLED,
'Call status was not CANCELLED');
}
$args = getopt('', array('server_host:', 'server_port:', 'test_case:')); $args = getopt('', array('server_host:', 'server_port:', 'test_case:'));
if (!array_key_exists('server_host', $args) || if (!array_key_exists('server_host', $args) ||
!array_key_exists('server_port', $args) || !array_key_exists('server_port', $args) ||
@ -187,4 +206,6 @@ switch($args['test_case']) {
case 'ping_pong': case 'ping_pong':
pingPong($stub); pingPong($stub);
break; break;
case 'cancel_after_first_response':
cancelAfterFirstResponse($stub);
} }

@ -1,16 +1,17 @@
<?php <?php
class CallTest extends PHPUnit_Framework_TestCase{ class CallTest extends PHPUnit_Framework_TestCase{
static $server; static $server;
static $port;
public static function setUpBeforeClass() { public static function setUpBeforeClass() {
$cq = new Grpc\CompletionQueue(); $cq = new Grpc\CompletionQueue();
self::$server = new Grpc\Server($cq, []); self::$server = new Grpc\Server($cq, []);
self::$server->add_http2_port('localhost:9001'); self::$port = self::$server->add_http2_port('0.0.0.0:0');
} }
public function setUp() { public function setUp() {
$this->cq = new Grpc\CompletionQueue(); $this->cq = new Grpc\CompletionQueue();
$this->channel = new Grpc\Channel('localhost:9001', []); $this->channel = new Grpc\Channel('localhost:' . self::$port, []);
$this->call = new Grpc\Call($this->channel, $this->call = new Grpc\Call($this->channel,
'/foo', '/foo',
Grpc\Timeval::inf_future()); Grpc\Timeval::inf_future());
@ -46,7 +47,7 @@ class CallTest extends PHPUnit_Framework_TestCase{
} }
public function testAddSingleMetadata() { public function testAddSingleMetadata() {
$this->call->add_metadata(['key' => 'value'], 0); $this->call->add_metadata(['key' => ['value']], 0);
/* Dummy assert: Checks that the previous call completed without error */ /* Dummy assert: Checks that the previous call completed without error */
$this->assertTrue(true); $this->assertTrue(true);
} }
@ -59,7 +60,7 @@ class CallTest extends PHPUnit_Framework_TestCase{
public function testAddSingleAndMultiValueMetadata() { public function testAddSingleAndMultiValueMetadata() {
$this->call->add_metadata( $this->call->add_metadata(
['key1' => 'value1', ['key1' => ['value1'],
'key2' => ['value2', 'value3']], 0); 'key2' => ['value2', 'value3']], 0);
/* Dummy assert: Checks that the previous call completed without error */ /* Dummy assert: Checks that the previous call completed without error */
$this->assertTrue(true); $this->assertTrue(true);

@ -1,13 +1,11 @@
<?php <?php
require __DIR__ . '/../util/port_picker.php';
class EndToEndTest extends PHPUnit_Framework_TestCase{ class EndToEndTest extends PHPUnit_Framework_TestCase{
public function setUp() { public function setUp() {
$this->client_queue = new Grpc\CompletionQueue(); $this->client_queue = new Grpc\CompletionQueue();
$this->server_queue = new Grpc\CompletionQueue(); $this->server_queue = new Grpc\CompletionQueue();
$this->server = new Grpc\Server($this->server_queue, []); $this->server = new Grpc\Server($this->server_queue, []);
$address = '127.0.0.1:' . getNewPort(); $port = $this->server->add_http2_port('0.0.0.0:0');
$this->server->add_http2_port($address); $this->channel = new Grpc\Channel('localhost:' . $port, []);
$this->channel = new Grpc\Channel($address, []);
} }
public function tearDown() { public function tearDown() {
@ -24,62 +22,52 @@ class EndToEndTest extends PHPUnit_Framework_TestCase{
'dummy_method', 'dummy_method',
$deadline); $deadline);
$tag = 1; $tag = 1;
$this->assertEquals(Grpc\CALL_OK, $call->invoke($this->client_queue, $tag, $tag);
$call->invoke($this->client_queue,
$tag,
$tag));
$server_tag = 2; $server_tag = 2;
$call->writes_done($tag); $call->writes_done($tag);
$event = $this->client_queue->next($deadline); $event = $this->client_queue->next($deadline);
$this->assertNotNull($event); $this->assertNotNull($event);
$this->assertEquals(Grpc\FINISH_ACCEPTED, $event->type); $this->assertSame(Grpc\FINISH_ACCEPTED, $event->type);
$this->assertEquals(Grpc\OP_OK, $event->data); $this->assertSame(Grpc\OP_OK, $event->data);
// check that a server rpc new was received // check that a server rpc new was received
$this->server->start(); $this->server->start();
$this->server->request_call($server_tag); $this->server->request_call($server_tag);
$event = $this->server_queue->next($deadline); $event = $this->server_queue->next($deadline);
$this->assertNotNull($event); $this->assertNotNull($event);
$this->assertEquals(Grpc\SERVER_RPC_NEW, $event->type); $this->assertSame(Grpc\SERVER_RPC_NEW, $event->type);
$server_call = $event->call; $server_call = $event->call;
$this->assertNotNull($server_call); $this->assertNotNull($server_call);
$this->assertEquals(Grpc\CALL_OK, $server_call->server_accept($this->server_queue, $server_tag);
$server_call->server_accept($this->server_queue,
$server_tag));
$this->assertEquals(Grpc\CALL_OK, $server_call->server_end_initial_metadata();
$server_call->server_end_initial_metadata());
// the server sends the status // the server sends the status
$this->assertEquals(Grpc\CALL_OK, $server_call->start_write_status(Grpc\STATUS_OK, $status_text, $server_tag);
$server_call->start_write_status(Grpc\STATUS_OK,
$status_text,
$server_tag));
$event = $this->server_queue->next($deadline); $event = $this->server_queue->next($deadline);
$this->assertNotNull($event); $this->assertNotNull($event);
$this->assertEquals(Grpc\FINISH_ACCEPTED, $event->type); $this->assertSame(Grpc\FINISH_ACCEPTED, $event->type);
$this->assertEquals(Grpc\OP_OK, $event->data); $this->assertSame(Grpc\OP_OK, $event->data);
// the client gets CLIENT_METADATA_READ // the client gets CLIENT_METADATA_READ
$event = $this->client_queue->next($deadline); $event = $this->client_queue->next($deadline);
$this->assertNotNull($event); $this->assertNotNull($event);
$this->assertEquals(Grpc\CLIENT_METADATA_READ, $event->type); $this->assertSame(Grpc\CLIENT_METADATA_READ, $event->type);
// the client gets FINISHED // the client gets FINISHED
$event = $this->client_queue->next($deadline); $event = $this->client_queue->next($deadline);
$this->assertNotNull($event); $this->assertNotNull($event);
$this->assertEquals(Grpc\FINISHED, $event->type); $this->assertSame(Grpc\FINISHED, $event->type);
$status = $event->data; $status = $event->data;
$this->assertEquals(Grpc\STATUS_OK, $status->code); $this->assertSame(Grpc\STATUS_OK, $status->code);
$this->assertEquals($status_text, $status->details); $this->assertSame($status_text, $status->details);
// and the server gets FINISHED // and the server gets FINISHED
$event = $this->server_queue->next($deadline); $event = $this->server_queue->next($deadline);
$this->assertNotNull($event); $this->assertNotNull($event);
$this->assertEquals(Grpc\FINISHED, $event->type); $this->assertSame(Grpc\FINISHED, $event->type);
$status = $event->data; $status = $event->data;
unset($call); unset($call);
@ -96,10 +84,7 @@ class EndToEndTest extends PHPUnit_Framework_TestCase{
'dummy_method', 'dummy_method',
$deadline); $deadline);
$tag = 1; $tag = 1;
$this->assertEquals(Grpc\CALL_OK, $call->invoke($this->client_queue, $tag, $tag);
$call->invoke($this->client_queue,
$tag,
$tag));
$server_tag = 2; $server_tag = 2;
@ -107,76 +92,69 @@ class EndToEndTest extends PHPUnit_Framework_TestCase{
$call->start_write($req_text, $tag); $call->start_write($req_text, $tag);
$event = $this->client_queue->next($deadline); $event = $this->client_queue->next($deadline);
$this->assertNotNull($event); $this->assertNotNull($event);
$this->assertEquals(Grpc\WRITE_ACCEPTED, $event->type); $this->assertSame(Grpc\WRITE_ACCEPTED, $event->type);
// check that a server rpc new was received // check that a server rpc new was received
$this->server->start(); $this->server->start();
$this->server->request_call($server_tag); $this->server->request_call($server_tag);
$event = $this->server_queue->next($deadline); $event = $this->server_queue->next($deadline);
$this->assertNotNull($event); $this->assertNotNull($event);
$this->assertEquals(Grpc\SERVER_RPC_NEW, $event->type); $this->assertSame(Grpc\SERVER_RPC_NEW, $event->type);
$server_call = $event->call; $server_call = $event->call;
$this->assertNotNull($server_call); $this->assertNotNull($server_call);
$this->assertEquals(Grpc\CALL_OK, $server_call->server_accept($this->server_queue, $server_tag);
$server_call->server_accept($this->server_queue,
$server_tag));
$this->assertEquals(Grpc\CALL_OK, $server_call->server_end_initial_metadata();
$server_call->server_end_initial_metadata());
// start the server read // start the server read
$server_call->start_read($server_tag); $server_call->start_read($server_tag);
$event = $this->server_queue->next($deadline); $event = $this->server_queue->next($deadline);
$this->assertNotNull($event); $this->assertNotNull($event);
$this->assertEquals(Grpc\READ, $event->type); $this->assertSame(Grpc\READ, $event->type);
$this->assertEquals($req_text, $event->data); $this->assertSame($req_text, $event->data);
// the server replies // the server replies
$this->assertEquals(Grpc\CALL_OK, $server_call->start_write($reply_text, $server_tag);
$server_call->start_write($reply_text, $server_tag));
$event = $this->server_queue->next($deadline); $event = $this->server_queue->next($deadline);
$this->assertNotNull($event); $this->assertNotNull($event);
$this->assertEquals(Grpc\WRITE_ACCEPTED, $event->type); $this->assertSame(Grpc\WRITE_ACCEPTED, $event->type);
// the client reads the metadata // the client reads the metadata
$event = $this->client_queue->next($deadline); $event = $this->client_queue->next($deadline);
$this->assertNotNull($event); $this->assertNotNull($event);
$this->assertEquals(Grpc\CLIENT_METADATA_READ, $event->type); $this->assertSame(Grpc\CLIENT_METADATA_READ, $event->type);
// the client reads the reply // the client reads the reply
$call->start_read($tag); $call->start_read($tag);
$event = $this->client_queue->next($deadline); $event = $this->client_queue->next($deadline);
$this->assertNotNull($event); $this->assertNotNull($event);
$this->assertEquals(Grpc\READ, $event->type); $this->assertSame(Grpc\READ, $event->type);
$this->assertEquals($reply_text, $event->data); $this->assertSame($reply_text, $event->data);
// the client sends writes done // the client sends writes done
$call->writes_done($tag); $call->writes_done($tag);
$event = $this->client_queue->next($deadline); $event = $this->client_queue->next($deadline);
$this->assertEquals(Grpc\FINISH_ACCEPTED, $event->type); $this->assertSame(Grpc\FINISH_ACCEPTED, $event->type);
$this->assertEquals(Grpc\OP_OK, $event->data); $this->assertSame(Grpc\OP_OK, $event->data);
// the server sends the status // the server sends the status
$this->assertEquals(Grpc\CALL_OK, $server_call->start_write_status(GRPC\STATUS_OK, $status_text, $server_tag);
$server_call->start_write_status(GRPC\STATUS_OK,
$status_text,
$server_tag));
$event = $this->server_queue->next($deadline); $event = $this->server_queue->next($deadline);
$this->assertEquals(Grpc\FINISH_ACCEPTED, $event->type); $this->assertSame(Grpc\FINISH_ACCEPTED, $event->type);
$this->assertEquals(Grpc\OP_OK, $event->data); $this->assertSame(Grpc\OP_OK, $event->data);
// the client gets FINISHED // the client gets FINISHED
$event = $this->client_queue->next($deadline); $event = $this->client_queue->next($deadline);
$this->assertNotNull($event); $this->assertNotNull($event);
$this->assertEquals(Grpc\FINISHED, $event->type); $this->assertSame(Grpc\FINISHED, $event->type);
$status = $event->data; $status = $event->data;
$this->assertEquals(Grpc\STATUS_OK, $status->code); $this->assertSame(Grpc\STATUS_OK, $status->code);
$this->assertEquals($status_text, $status->details); $this->assertSame($status_text, $status->details);
// and the server gets FINISHED // and the server gets FINISHED
$event = $this->server_queue->next($deadline); $event = $this->server_queue->next($deadline);
$this->assertNotNull($event); $this->assertNotNull($event);
$this->assertEquals(Grpc\FINISHED, $event->type); $this->assertSame(Grpc\FINISHED, $event->type);
unset($call); unset($call);
unset($server_call); unset($server_call);

@ -11,10 +11,9 @@ class SecureEndToEndTest extends PHPUnit_Framework_TestCase{
file_get_contents(dirname(__FILE__) . '/../data/server1.pem')); file_get_contents(dirname(__FILE__) . '/../data/server1.pem'));
$this->server = new Grpc\Server($this->server_queue, $this->server = new Grpc\Server($this->server_queue,
['credentials' => $server_credentials]); ['credentials' => $server_credentials]);
$address = '127.0.0.1:' . getNewPort(); $port = $this->server->add_secure_http2_port('0.0.0.0:0');
$this->server->add_secure_http2_port($address);
$this->channel = new Grpc\Channel( $this->channel = new Grpc\Channel(
$address, 'localhost:' . $port,
[ [
'grpc.ssl_target_name_override' => 'foo.test.google.com', 'grpc.ssl_target_name_override' => 'foo.test.google.com',
'credentials' => $credentials 'credentials' => $credentials
@ -36,59 +35,50 @@ class SecureEndToEndTest extends PHPUnit_Framework_TestCase{
'dummy_method', 'dummy_method',
$deadline); $deadline);
$tag = 1; $tag = 1;
$this->assertEquals(Grpc\CALL_OK, $call->invoke($this->client_queue, $tag, $tag);
$call->invoke($this->client_queue,
$tag,
$tag));
$server_tag = 2; $server_tag = 2;
$call->writes_done($tag); $call->writes_done($tag);
$event = $this->client_queue->next($deadline); $event = $this->client_queue->next($deadline);
$this->assertNotNull($event); $this->assertNotNull($event);
$this->assertEquals(Grpc\FINISH_ACCEPTED, $event->type); $this->assertSame(Grpc\FINISH_ACCEPTED, $event->type);
$this->assertEquals(Grpc\OP_OK, $event->data); $this->assertSame(Grpc\OP_OK, $event->data);
// check that a server rpc new was received // check that a server rpc new was received
$this->server->request_call($server_tag); $this->server->request_call($server_tag);
$event = $this->server_queue->next($deadline); $event = $this->server_queue->next($deadline);
$this->assertNotNull($event); $this->assertNotNull($event);
$this->assertEquals(Grpc\SERVER_RPC_NEW, $event->type); $this->assertSame(Grpc\SERVER_RPC_NEW, $event->type);
$server_call = $event->call; $server_call = $event->call;
$this->assertNotNull($server_call); $this->assertNotNull($server_call);
$this->assertEquals(Grpc\CALL_OK, $server_call->server_accept($this->server_queue, $server_tag);
$server_call->server_accept($this->server_queue,
$server_tag));
$this->assertEquals(Grpc\CALL_OK, $server_call->server_end_initial_metadata();
$server_call->server_end_initial_metadata());
// the server sends the status // the server sends the status
$this->assertEquals(Grpc\CALL_OK, $server_call->start_write_status(Grpc\STATUS_OK, $status_text, $server_tag);
$server_call->start_write_status(Grpc\STATUS_OK,
$status_text,
$server_tag));
$event = $this->server_queue->next($deadline); $event = $this->server_queue->next($deadline);
$this->assertNotNull($event); $this->assertNotNull($event);
$this->assertEquals(Grpc\FINISH_ACCEPTED, $event->type); $this->assertSame(Grpc\FINISH_ACCEPTED, $event->type);
$this->assertEquals(Grpc\OP_OK, $event->data); $this->assertSame(Grpc\OP_OK, $event->data);
// the client gets CLIENT_METADATA_READ // the client gets CLIENT_METADATA_READ
$event = $this->client_queue->next($deadline); $event = $this->client_queue->next($deadline);
$this->assertNotNull($event); $this->assertNotNull($event);
$this->assertEquals(Grpc\CLIENT_METADATA_READ, $event->type); $this->assertSame(Grpc\CLIENT_METADATA_READ, $event->type);
// the client gets FINISHED // the client gets FINISHED
$event = $this->client_queue->next($deadline); $event = $this->client_queue->next($deadline);
$this->assertNotNull($event); $this->assertNotNull($event);
$this->assertEquals(Grpc\FINISHED, $event->type); $this->assertSame(Grpc\FINISHED, $event->type);
$status = $event->data; $status = $event->data;
$this->assertEquals(Grpc\STATUS_OK, $status->code); $this->assertSame(Grpc\STATUS_OK, $status->code);
$this->assertEquals($status_text, $status->details); $this->assertSame($status_text, $status->details);
// and the server gets FINISHED // and the server gets FINISHED
$event = $this->server_queue->next($deadline); $event = $this->server_queue->next($deadline);
$this->assertNotNull($event); $this->assertNotNull($event);
$this->assertEquals(Grpc\FINISHED, $event->type); $this->assertSame(Grpc\FINISHED, $event->type);
$status = $event->data; $status = $event->data;
unset($call); unset($call);
@ -106,10 +96,7 @@ class SecureEndToEndTest extends PHPUnit_Framework_TestCase{
'dummy_method', 'dummy_method',
$deadline); $deadline);
$tag = 1; $tag = 1;
$this->assertEquals(Grpc\CALL_OK, $call->invoke($this->client_queue, $tag, $tag);
$call->invoke($this->client_queue,
$tag,
$tag));
$server_tag = 2; $server_tag = 2;
@ -117,75 +104,68 @@ class SecureEndToEndTest extends PHPUnit_Framework_TestCase{
$call->start_write($req_text, $tag); $call->start_write($req_text, $tag);
$event = $this->client_queue->next($deadline); $event = $this->client_queue->next($deadline);
$this->assertNotNull($event); $this->assertNotNull($event);
$this->assertEquals(Grpc\WRITE_ACCEPTED, $event->type); $this->assertSame(Grpc\WRITE_ACCEPTED, $event->type);
// check that a server rpc new was received // check that a server rpc new was received
$this->server->request_call($server_tag); $this->server->request_call($server_tag);
$event = $this->server_queue->next($deadline); $event = $this->server_queue->next($deadline);
$this->assertNotNull($event); $this->assertNotNull($event);
$this->assertEquals(Grpc\SERVER_RPC_NEW, $event->type); $this->assertSame(Grpc\SERVER_RPC_NEW, $event->type);
$server_call = $event->call; $server_call = $event->call;
$this->assertNotNull($server_call); $this->assertNotNull($server_call);
$this->assertEquals(Grpc\CALL_OK, $server_call->server_accept($this->server_queue, $server_tag);
$server_call->server_accept($this->server_queue,
$server_tag));
$this->assertEquals(Grpc\CALL_OK, $server_call->server_end_initial_metadata();
$server_call->server_end_initial_metadata());
// start the server read // start the server read
$server_call->start_read($server_tag); $server_call->start_read($server_tag);
$event = $this->server_queue->next($deadline); $event = $this->server_queue->next($deadline);
$this->assertNotNull($event); $this->assertNotNull($event);
$this->assertEquals(Grpc\READ, $event->type); $this->assertSame(Grpc\READ, $event->type);
$this->assertEquals($req_text, $event->data); $this->assertSame($req_text, $event->data);
// the server replies // the server replies
$this->assertEquals(Grpc\CALL_OK, $server_call->start_write($reply_text, $server_tag);
$server_call->start_write($reply_text, $server_tag));
$event = $this->server_queue->next($deadline); $event = $this->server_queue->next($deadline);
$this->assertNotNull($event); $this->assertNotNull($event);
$this->assertEquals(Grpc\WRITE_ACCEPTED, $event->type); $this->assertSame(Grpc\WRITE_ACCEPTED, $event->type);
// the client reads the metadata // the client reads the metadata
$event = $this->client_queue->next($deadline); $event = $this->client_queue->next($deadline);
$this->assertNotNull($event); $this->assertNotNull($event);
$this->assertEquals(Grpc\CLIENT_METADATA_READ, $event->type); $this->assertSame(Grpc\CLIENT_METADATA_READ, $event->type);
// the client reads the reply // the client reads the reply
$call->start_read($tag); $call->start_read($tag);
$event = $this->client_queue->next($deadline); $event = $this->client_queue->next($deadline);
$this->assertNotNull($event); $this->assertNotNull($event);
$this->assertEquals(Grpc\READ, $event->type); $this->assertSame(Grpc\READ, $event->type);
$this->assertEquals($reply_text, $event->data); $this->assertSame($reply_text, $event->data);
// the client sends writes done // the client sends writes done
$call->writes_done($tag); $call->writes_done($tag);
$event = $this->client_queue->next($deadline); $event = $this->client_queue->next($deadline);
$this->assertEquals(Grpc\FINISH_ACCEPTED, $event->type); $this->assertSame(Grpc\FINISH_ACCEPTED, $event->type);
$this->assertEquals(Grpc\OP_OK, $event->data); $this->assertSame(Grpc\OP_OK, $event->data);
// the server sends the status // the server sends the status
$this->assertEquals(Grpc\CALL_OK, $server_call->start_write_status(GRPC\STATUS_OK, $status_text, $server_tag);
$server_call->start_write_status(GRPC\STATUS_OK,
$status_text,
$server_tag));
$event = $this->server_queue->next($deadline); $event = $this->server_queue->next($deadline);
$this->assertEquals(Grpc\FINISH_ACCEPTED, $event->type); $this->assertSame(Grpc\FINISH_ACCEPTED, $event->type);
$this->assertEquals(Grpc\OP_OK, $event->data); $this->assertSame(Grpc\OP_OK, $event->data);
// the client gets FINISHED // the client gets FINISHED
$event = $this->client_queue->next($deadline); $event = $this->client_queue->next($deadline);
$this->assertNotNull($event); $this->assertNotNull($event);
$this->assertEquals(Grpc\FINISHED, $event->type); $this->assertSame(Grpc\FINISHED, $event->type);
$status = $event->data; $status = $event->data;
$this->assertEquals(Grpc\STATUS_OK, $status->code); $this->assertSame(Grpc\STATUS_OK, $status->code);
$this->assertEquals($status_text, $status->details); $this->assertSame($status_text, $status->details);
// and the server gets FINISHED // and the server gets FINISHED
$event = $this->server_queue->next($deadline); $event = $this->server_queue->next($deadline);
$this->assertNotNull($event); $this->assertNotNull($event);
$this->assertEquals(Grpc\FINISHED, $event->type); $this->assertSame(Grpc\FINISHED, $event->type);
unset($call); unset($call);
unset($server_call); unset($server_call);

@ -2,7 +2,7 @@
class TimevalTest extends PHPUnit_Framework_TestCase{ class TimevalTest extends PHPUnit_Framework_TestCase{
public function testCompareSame() { public function testCompareSame() {
$zero = Grpc\Timeval::zero(); $zero = Grpc\Timeval::zero();
$this->assertEquals(0, Grpc\Timeval::compare($zero, $zero)); $this->assertSame(0, Grpc\Timeval::compare($zero, $zero));
} }
public function testPastIsLessThanZero() { public function testPastIsLessThanZero() {

@ -1,6 +0,0 @@
<?php
function getNewPort() {
static $port = 10000;
$port += 1;
return $port;
}

@ -56,8 +56,8 @@ static int pygrpc_call_init(Call *self, PyObject *args, PyObject *kwds) {
/* TODO(nathaniel): Hoist the gpr_timespec <-> PyFloat arithmetic into its own /* TODO(nathaniel): Hoist the gpr_timespec <-> PyFloat arithmetic into its own
* function with its own test coverage. * function with its own test coverage.
*/ */
self->c_call = self->c_call = grpc_channel_create_call_old(
grpc_channel_create_call(((Channel *)channel)->c_channel, method, host, ((Channel *)channel)->c_channel, method, host,
gpr_time_from_nanos(deadline * GPR_NS_PER_SEC)); gpr_time_from_nanos(deadline * GPR_NS_PER_SEC));
return 0; return 0;
@ -82,7 +82,7 @@ static const PyObject *pygrpc_call_invoke(Call *self, PyObject *args) {
return NULL; return NULL;
} }
call_error = grpc_call_invoke( call_error = grpc_call_invoke_old(
self->c_call, ((CompletionQueue *)completion_queue)->c_completion_queue, self->c_call, ((CompletionQueue *)completion_queue)->c_completion_queue,
(void *)metadata_tag, (void *)finish_tag, 0); (void *)metadata_tag, (void *)finish_tag, 0);
@ -111,7 +111,8 @@ static const PyObject *pygrpc_call_write(Call *self, PyObject *args) {
byte_buffer = grpc_byte_buffer_create(&slice, 1); byte_buffer = grpc_byte_buffer_create(&slice, 1);
gpr_slice_unref(slice); gpr_slice_unref(slice);
call_error = grpc_call_start_write(self->c_call, byte_buffer, (void *)tag, 0); call_error =
grpc_call_start_write_old(self->c_call, byte_buffer, (void *)tag, 0);
grpc_byte_buffer_destroy(byte_buffer); grpc_byte_buffer_destroy(byte_buffer);
@ -131,7 +132,7 @@ static const PyObject *pygrpc_call_complete(Call *self, PyObject *args) {
return NULL; return NULL;
} }
call_error = grpc_call_writes_done(self->c_call, (void *)tag); call_error = grpc_call_writes_done_old(self->c_call, (void *)tag);
result = pygrpc_translate_call_error(call_error); result = pygrpc_translate_call_error(call_error);
if (result != NULL) { if (result != NULL) {
@ -151,7 +152,7 @@ static const PyObject *pygrpc_call_accept(Call *self, PyObject *args) {
return NULL; return NULL;
} }
call_error = grpc_call_server_accept( call_error = grpc_call_server_accept_old(
self->c_call, ((CompletionQueue *)completion_queue)->c_completion_queue, self->c_call, ((CompletionQueue *)completion_queue)->c_completion_queue,
(void *)tag); (void *)tag);
result = pygrpc_translate_call_error(call_error); result = pygrpc_translate_call_error(call_error);
@ -166,7 +167,7 @@ static const PyObject *pygrpc_call_accept(Call *self, PyObject *args) {
static const PyObject *pygrpc_call_premetadata(Call *self, PyObject *args) { static const PyObject *pygrpc_call_premetadata(Call *self, PyObject *args) {
/* TODO(b/18702680): Actually support metadata. */ /* TODO(b/18702680): Actually support metadata. */
return pygrpc_translate_call_error( return pygrpc_translate_call_error(
grpc_call_server_end_initial_metadata(self->c_call, 0)); grpc_call_server_end_initial_metadata_old(self->c_call, 0));
} }
static const PyObject *pygrpc_call_read(Call *self, PyObject *args) { static const PyObject *pygrpc_call_read(Call *self, PyObject *args) {
@ -178,7 +179,7 @@ static const PyObject *pygrpc_call_read(Call *self, PyObject *args) {
return NULL; return NULL;
} }
call_error = grpc_call_start_read(self->c_call, (void *)tag); call_error = grpc_call_start_read_old(self->c_call, (void *)tag);
result = pygrpc_translate_call_error(call_error); result = pygrpc_translate_call_error(call_error);
if (result != NULL) { if (result != NULL) {
@ -208,7 +209,7 @@ static const PyObject *pygrpc_call_status(Call *self, PyObject *args) {
Py_DECREF(code); Py_DECREF(code);
Py_DECREF(details); Py_DECREF(details);
call_error = grpc_call_start_write_status(self->c_call, c_code, c_message, call_error = grpc_call_start_write_status_old(self->c_call, c_code, c_message,
(void *)tag); (void *)tag);
result = pygrpc_translate_call_error(call_error); result = pygrpc_translate_call_error(call_error);

@ -80,8 +80,8 @@ class RoundTripTest(unittest.TestCase):
rear_link.start() rear_link.start()
front_to_back_ticket = tickets.FrontToBackPacket( front_to_back_ticket = tickets.FrontToBackPacket(
test_operation_id, 0, tickets.Kind.ENTIRE, test_method, interfaces.FULL, test_operation_id, 0, tickets.Kind.ENTIRE, test_method,
None, None, _TIMEOUT) interfaces.ServicedSubscription.Kind.FULL, None, None, _TIMEOUT)
rear_link.accept_front_to_back_ticket(front_to_back_ticket) rear_link.accept_front_to_back_ticket(front_to_back_ticket)
with test_fore_link.condition: with test_fore_link.condition:
@ -133,8 +133,9 @@ class RoundTripTest(unittest.TestCase):
rear_link.start() rear_link.start()
front_to_back_ticket = tickets.FrontToBackPacket( front_to_back_ticket = tickets.FrontToBackPacket(
test_operation_id, 0, tickets.Kind.ENTIRE, test_method, interfaces.FULL, test_operation_id, 0, tickets.Kind.ENTIRE, test_method,
None, test_front_to_back_datum, _TIMEOUT) interfaces.ServicedSubscription.Kind.FULL, None,
test_front_to_back_datum, _TIMEOUT)
rear_link.accept_front_to_back_ticket(front_to_back_ticket) rear_link.accept_front_to_back_ticket(front_to_back_ticket)
with test_fore_link.condition: with test_fore_link.condition:
@ -196,7 +197,7 @@ class RoundTripTest(unittest.TestCase):
commencement_ticket = tickets.FrontToBackPacket( commencement_ticket = tickets.FrontToBackPacket(
test_operation_id, 0, tickets.Kind.COMMENCEMENT, test_method, test_operation_id, 0, tickets.Kind.COMMENCEMENT, test_method,
interfaces.FULL, None, None, _TIMEOUT) interfaces.ServicedSubscription.Kind.FULL, None, None, _TIMEOUT)
fore_sequence_number = 1 fore_sequence_number = 1
rear_link.accept_front_to_back_ticket(commencement_ticket) rear_link.accept_front_to_back_ticket(commencement_ticket)
for request in scenario.requests(): for request in scenario.requests():

@ -69,7 +69,7 @@ class LonelyRearLinkTest(unittest.TestCase):
front_to_back_ticket = packets.FrontToBackPacket( front_to_back_ticket = packets.FrontToBackPacket(
test_operation_id, 0, front_to_back_ticket_kind, test_method, test_operation_id, 0, front_to_back_ticket_kind, test_method,
interfaces.FULL, None, None, _TIMEOUT) interfaces.ServicedSubscription.Kind.FULL, None, None, _TIMEOUT)
rear_link.accept_front_to_back_ticket(front_to_back_ticket) rear_link.accept_front_to_back_ticket(front_to_back_ticket)
with fore_link.condition: with fore_link.condition:

@ -88,7 +88,7 @@ static const PyObject *pygrpc_server_service(Server *self, PyObject *args) {
return NULL; return NULL;
} }
call_error = grpc_server_request_call(self->c_server, (void *)tag); call_error = grpc_server_request_call_old(self->c_server, (void *)tag);
result = pygrpc_translate_call_error(call_error); result = pygrpc_translate_call_error(call_error);
if (result != NULL) { if (result != NULL) {

@ -116,7 +116,8 @@ class ForeLink(ticket_interfaces.ForeLink):
self._response_serializers[method]) self._response_serializers[method])
ticket = tickets.FrontToBackPacket( ticket = tickets.FrontToBackPacket(
call, 0, tickets.Kind.COMMENCEMENT, method, interfaces.FULL, None, None, call, 0, tickets.Kind.COMMENCEMENT, method,
interfaces.ServicedSubscription.Kind.FULL, None, None,
service_acceptance.deadline - time.time()) service_acceptance.deadline - time.time())
self._rear_link.accept_front_to_back_ticket(ticket) self._rear_link.accept_front_to_back_ticket(ticket)

@ -29,15 +29,17 @@
"""Interfaces defined and used by the base layer of RPC Framework.""" """Interfaces defined and used by the base layer of RPC Framework."""
# TODO(nathaniel): Use Python's new enum library for enumerated types rather
# than constants merely placed close together.
import abc import abc
import enum
# stream is referenced from specification in this module. # stream is referenced from specification in this module.
from _framework.foundation import stream # pylint: disable=unused-import from _framework.foundation import stream # pylint: disable=unused-import
# Operation outcomes.
@enum.unique
class Outcome(enum.Enum):
"""Operation outcomes."""
COMPLETED = 'completed' COMPLETED = 'completed'
CANCELLED = 'cancelled' CANCELLED = 'cancelled'
EXPIRED = 'expired' EXPIRED = 'expired'
@ -46,11 +48,6 @@ TRANSMISSION_FAILURE = 'transmission failure'
SERVICER_FAILURE = 'servicer failure' SERVICER_FAILURE = 'servicer failure'
SERVICED_FAILURE = 'serviced failure' SERVICED_FAILURE = 'serviced failure'
# Subscription categories.
FULL = 'full'
TERMINATION_ONLY = 'termination only'
NONE = 'none'
class OperationContext(object): class OperationContext(object):
"""Provides operation-related information and action. """Provides operation-related information and action.
@ -70,9 +67,7 @@ class OperationContext(object):
"""Adds a function to be called upon operation termination. """Adds a function to be called upon operation termination.
Args: Args:
callback: A callable that will be passed one of COMPLETED, CANCELLED, callback: A callable that will be passed an Outcome value.
EXPIRED, RECEPTION_FAILURE, TRANSMISSION_FAILURE, SERVICER_FAILURE, or
SERVICED_FAILURE.
""" """
raise NotImplementedError() raise NotImplementedError()
@ -167,11 +162,20 @@ class ServicedSubscription(object):
"""A sum type representing a serviced's interest in an operation. """A sum type representing a serviced's interest in an operation.
Attributes: Attributes:
category: One of FULL, TERMINATION_ONLY, or NONE. kind: A Kind value.
ingestor: A ServicedIngestor. Must be present if category is FULL. ingestor: A ServicedIngestor. Must be present if kind is Kind.FULL. Must
be None if kind is Kind.TERMINATION_ONLY or Kind.NONE.
""" """
__metaclass__ = abc.ABCMeta __metaclass__ = abc.ABCMeta
@enum.unique
class Kind(enum.Enum):
"""Kinds of subscription."""
FULL = 'full'
TERMINATION_ONLY = 'termination only'
NONE = 'none'
class End(object): class End(object):
"""Common type for entry-point objects on both sides of an operation.""" """Common type for entry-point objects on both sides of an operation."""
@ -182,9 +186,8 @@ class End(object):
"""Reports the number of terminated operations broken down by outcome. """Reports the number of terminated operations broken down by outcome.
Returns: Returns:
A dictionary from operation outcome constant (COMPLETED, CANCELLED, A dictionary from Outcome value to an integer identifying the number
EXPIRED, and so on) to an integer representing the number of operations of operations that terminated with that outcome.
that terminated with that outcome.
""" """
raise NotImplementedError() raise NotImplementedError()

@ -49,13 +49,13 @@ TRIGGERED_FAILURE = 'triggered failure'
WAIT_ON_CONDITION = 'wait on condition' WAIT_ON_CONDITION = 'wait on condition'
EMPTY_OUTCOME_DICT = { EMPTY_OUTCOME_DICT = {
interfaces.COMPLETED: 0, interfaces.Outcome.COMPLETED: 0,
interfaces.CANCELLED: 0, interfaces.Outcome.CANCELLED: 0,
interfaces.EXPIRED: 0, interfaces.Outcome.EXPIRED: 0,
interfaces.RECEPTION_FAILURE: 0, interfaces.Outcome.RECEPTION_FAILURE: 0,
interfaces.TRANSMISSION_FAILURE: 0, interfaces.Outcome.TRANSMISSION_FAILURE: 0,
interfaces.SERVICER_FAILURE: 0, interfaces.Outcome.SERVICER_FAILURE: 0,
interfaces.SERVICED_FAILURE: 0, interfaces.Outcome.SERVICED_FAILURE: 0,
} }
@ -169,7 +169,8 @@ class FrontAndBackTest(object):
SYNCHRONOUS_ECHO, None, True, SMALL_TIMEOUT, SYNCHRONOUS_ECHO, None, True, SMALL_TIMEOUT,
util.none_serviced_subscription(), 'test trace ID') util.none_serviced_subscription(), 'test trace ID')
util.wait_for_idle(self.front) util.wait_for_idle(self.front)
self.assertEqual(1, self.front.operation_stats()[interfaces.COMPLETED]) self.assertEqual(
1, self.front.operation_stats()[interfaces.Outcome.COMPLETED])
# Assuming nothing really pathological (such as pauses on the order of # Assuming nothing really pathological (such as pauses on the order of
# SMALL_TIMEOUT interfering with this test) there are a two different ways # SMALL_TIMEOUT interfering with this test) there are a two different ways
@ -183,7 +184,7 @@ class FrontAndBackTest(object):
first_back_possibility = EMPTY_OUTCOME_DICT first_back_possibility = EMPTY_OUTCOME_DICT
# (2) The packet arrived at the back and the back completed the operation. # (2) The packet arrived at the back and the back completed the operation.
second_back_possibility = dict(EMPTY_OUTCOME_DICT) second_back_possibility = dict(EMPTY_OUTCOME_DICT)
second_back_possibility[interfaces.COMPLETED] = 1 second_back_possibility[interfaces.Outcome.COMPLETED] = 1
self.assertIn( self.assertIn(
back_operation_stats, (first_back_possibility, second_back_possibility)) back_operation_stats, (first_back_possibility, second_back_possibility))
# It's true that if the packet had arrived at the back and the back had # It's true that if the packet had arrived at the back and the back had
@ -204,8 +205,10 @@ class FrontAndBackTest(object):
util.wait_for_idle(self.front) util.wait_for_idle(self.front)
util.wait_for_idle(self.back) util.wait_for_idle(self.back)
self.assertEqual(1, self.front.operation_stats()[interfaces.COMPLETED]) self.assertEqual(
self.assertEqual(1, self.back.operation_stats()[interfaces.COMPLETED]) 1, self.front.operation_stats()[interfaces.Outcome.COMPLETED])
self.assertEqual(
1, self.back.operation_stats()[interfaces.Outcome.COMPLETED])
self.assertListEqual([(test_payload, True)], test_consumer.calls) self.assertListEqual([(test_payload, True)], test_consumer.calls)
def testBidirectionalStreamingEcho(self): def testBidirectionalStreamingEcho(self):
@ -226,8 +229,10 @@ class FrontAndBackTest(object):
util.wait_for_idle(self.front) util.wait_for_idle(self.front)
util.wait_for_idle(self.back) util.wait_for_idle(self.back)
self.assertEqual(1, self.front.operation_stats()[interfaces.COMPLETED]) self.assertEqual(
self.assertEqual(1, self.back.operation_stats()[interfaces.COMPLETED]) 1, self.front.operation_stats()[interfaces.Outcome.COMPLETED])
self.assertEqual(
1, self.back.operation_stats()[interfaces.Outcome.COMPLETED])
self.assertListEqual(test_payloads, test_consumer.values()) self.assertListEqual(test_payloads, test_consumer.values())
def testCancellation(self): def testCancellation(self):
@ -242,7 +247,8 @@ class FrontAndBackTest(object):
operation.cancel() operation.cancel()
util.wait_for_idle(self.front) util.wait_for_idle(self.front)
self.assertEqual(1, self.front.operation_stats()[interfaces.CANCELLED]) self.assertEqual(
1, self.front.operation_stats()[interfaces.Outcome.CANCELLED])
util.wait_for_idle(self.back) util.wait_for_idle(self.back)
self.assertListEqual([], test_consumer.calls) self.assertListEqual([], test_consumer.calls)
@ -260,7 +266,7 @@ class FrontAndBackTest(object):
# The back started processing based on the first packet and then stopped # The back started processing based on the first packet and then stopped
# upon receiving the cancellation packet. # upon receiving the cancellation packet.
second_back_possibility = dict(EMPTY_OUTCOME_DICT) second_back_possibility = dict(EMPTY_OUTCOME_DICT)
second_back_possibility[interfaces.CANCELLED] = 1 second_back_possibility[interfaces.Outcome.CANCELLED] = 1
self.assertIn( self.assertIn(
back_operation_stats, (first_back_possibility, second_back_possibility)) back_operation_stats, (first_back_possibility, second_back_possibility))
@ -292,8 +298,10 @@ class FrontAndBackTest(object):
duration = termination_time_cell[0] - start_time duration = termination_time_cell[0] - start_time
self.assertLessEqual(timeout, duration) self.assertLessEqual(timeout, duration)
self.assertLess(duration, timeout + allowance) self.assertLess(duration, timeout + allowance)
self.assertEqual(interfaces.EXPIRED, outcome_cell[0]) self.assertEqual(interfaces.Outcome.EXPIRED, outcome_cell[0])
util.wait_for_idle(self.front) util.wait_for_idle(self.front)
self.assertEqual(1, self.front.operation_stats()[interfaces.EXPIRED]) self.assertEqual(
1, self.front.operation_stats()[interfaces.Outcome.EXPIRED])
util.wait_for_idle(self.back) util.wait_for_idle(self.back)
self.assertLessEqual(1, self.back.operation_stats()[interfaces.EXPIRED]) self.assertLessEqual(
1, self.back.operation_stats()[interfaces.Outcome.EXPIRED])

@ -51,13 +51,13 @@ from _framework.foundation import callable_util
_IDLE_ACTION_EXCEPTION_LOG_MESSAGE = 'Exception calling idle action!' _IDLE_ACTION_EXCEPTION_LOG_MESSAGE = 'Exception calling idle action!'
_OPERATION_OUTCOMES = ( _OPERATION_OUTCOMES = (
base_interfaces.COMPLETED, base_interfaces.Outcome.COMPLETED,
base_interfaces.CANCELLED, base_interfaces.Outcome.CANCELLED,
base_interfaces.EXPIRED, base_interfaces.Outcome.EXPIRED,
base_interfaces.RECEPTION_FAILURE, base_interfaces.Outcome.RECEPTION_FAILURE,
base_interfaces.TRANSMISSION_FAILURE, base_interfaces.Outcome.TRANSMISSION_FAILURE,
base_interfaces.SERVICER_FAILURE, base_interfaces.Outcome.SERVICER_FAILURE,
base_interfaces.SERVICED_FAILURE, base_interfaces.Outcome.SERVICED_FAILURE,
) )
@ -193,10 +193,10 @@ def _front_operate(
lock = threading.Lock() lock = threading.Lock()
with lock: with lock:
termination_manager = _termination.front_termination_manager( termination_manager = _termination.front_termination_manager(
work_pool, utility_pool, termination_action, subscription.category) work_pool, utility_pool, termination_action, subscription.kind)
transmission_manager = _transmission.front_transmission_manager( transmission_manager = _transmission.front_transmission_manager(
lock, transmission_pool, callback, operation_id, name, lock, transmission_pool, callback, operation_id, name,
subscription.category, trace_id, timeout, termination_manager) subscription.kind, trace_id, timeout, termination_manager)
operation_context = _context.OperationContext( operation_context = _context.OperationContext(
lock, operation_id, packets.Kind.SERVICED_FAILURE, lock, operation_id, packets.Kind.SERVICED_FAILURE,
termination_manager, transmission_manager) termination_manager, transmission_manager)
@ -225,9 +225,10 @@ def _front_operate(
transmission_manager.inmit(payload, complete) transmission_manager.inmit(payload, complete)
returned_reception_manager = ( if subscription.kind is base_interfaces.ServicedSubscription.Kind.NONE:
None if subscription.category == base_interfaces.NONE returned_reception_manager = None
else reception_manager) else:
returned_reception_manager = reception_manager
return _FrontManagement( return _FrontManagement(
returned_reception_manager, emission_manager, operation_context, returned_reception_manager, emission_manager, operation_context,

@ -111,7 +111,7 @@ class _FrontConsumerCreator(_ConsumerCreator):
def create_consumer(self, requirement): def create_consumer(self, requirement):
"""See _ConsumerCreator.create_consumer for specification.""" """See _ConsumerCreator.create_consumer for specification."""
if self._subscription.category == interfaces.FULL: if self._subscription.kind is interfaces.ServicedSubscription.Kind.FULL:
try: try:
return _ConsumerCreation( return _ConsumerCreation(
self._subscription.ingestor.consumer(self._operation_context), self._subscription.ingestor.consumer(self._operation_context),

@ -58,10 +58,7 @@ class TerminationManager(object):
immediately. immediately.
Args: Args:
callback: A callable that will be passed one of base_interfaces.COMPLETED, callback: A callable that will be passed a base_interfaces.Outcome value.
base_interfaces.CANCELLED, base_interfaces.EXPIRED,
base_interfaces.RECEPTION_FAILURE, base_interfaces.TRANSMISSION_FAILURE,
base_interfaces.SERVICER_FAILURE, or base_interfaces.SERVICED_FAILURE.
""" """
raise NotImplementedError() raise NotImplementedError()

@ -29,6 +29,8 @@
"""State and behavior for operation termination.""" """State and behavior for operation termination."""
import enum
from _framework.base import interfaces from _framework.base import interfaces
from _framework.base.packets import _constants from _framework.base.packets import _constants
from _framework.base.packets import _interfaces from _framework.base.packets import _interfaces
@ -37,26 +39,32 @@ from _framework.foundation import callable_util
_CALLBACK_EXCEPTION_LOG_MESSAGE = 'Exception calling termination callback!' _CALLBACK_EXCEPTION_LOG_MESSAGE = 'Exception calling termination callback!'
# TODO(nathaniel): enum module.
_EMISSION = 'emission'
_TRANSMISSION = 'transmission'
_INGESTION = 'ingestion'
_FRONT_NOT_LISTENING_REQUIREMENTS = (_TRANSMISSION,)
_BACK_NOT_LISTENING_REQUIREMENTS = (_EMISSION, _INGESTION,)
_LISTENING_REQUIREMENTS = (_TRANSMISSION, _INGESTION,)
_KINDS_TO_OUTCOMES = { _KINDS_TO_OUTCOMES = {
packets.Kind.COMPLETION: interfaces.COMPLETED, packets.Kind.COMPLETION: interfaces.Outcome.COMPLETED,
packets.Kind.CANCELLATION: interfaces.CANCELLED, packets.Kind.CANCELLATION: interfaces.Outcome.CANCELLED,
packets.Kind.EXPIRATION: interfaces.EXPIRED, packets.Kind.EXPIRATION: interfaces.Outcome.EXPIRED,
packets.Kind.RECEPTION_FAILURE: interfaces.RECEPTION_FAILURE, packets.Kind.RECEPTION_FAILURE: interfaces.Outcome.RECEPTION_FAILURE,
packets.Kind.TRANSMISSION_FAILURE: interfaces.TRANSMISSION_FAILURE, packets.Kind.TRANSMISSION_FAILURE: interfaces.Outcome.TRANSMISSION_FAILURE,
packets.Kind.SERVICER_FAILURE: interfaces.SERVICER_FAILURE, packets.Kind.SERVICER_FAILURE: interfaces.Outcome.SERVICER_FAILURE,
packets.Kind.SERVICED_FAILURE: interfaces.SERVICED_FAILURE, packets.Kind.SERVICED_FAILURE: interfaces.Outcome.SERVICED_FAILURE,
} }
@enum.unique
class _Requirement(enum.Enum):
"""Symbols indicating events required for termination."""
EMISSION = 'emission'
TRANSMISSION = 'transmission'
INGESTION = 'ingestion'
_FRONT_NOT_LISTENING_REQUIREMENTS = (_Requirement.TRANSMISSION,)
_BACK_NOT_LISTENING_REQUIREMENTS = (
_Requirement.EMISSION, _Requirement.INGESTION,)
_LISTENING_REQUIREMENTS = (
_Requirement.TRANSMISSION, _Requirement.INGESTION,)
class _TerminationManager(_interfaces.TerminationManager): class _TerminationManager(_interfaces.TerminationManager):
"""An implementation of _interfaces.TerminationManager.""" """An implementation of _interfaces.TerminationManager."""
@ -68,9 +76,8 @@ class _TerminationManager(_interfaces.TerminationManager):
work_pool: A thread pool in which customer work will be done. work_pool: A thread pool in which customer work will be done.
utility_pool: A thread pool in which work utility work will be done. utility_pool: A thread pool in which work utility work will be done.
action: An action to call on operation termination. action: An action to call on operation termination.
requirements: A combination of _EMISSION, _TRANSMISSION, and _INGESTION requirements: A combination of _Requirement values identifying what
identifying what must finish for the operation to be considered must finish for the operation to be considered completed.
completed.
local_failure: A packets.Kind specifying what constitutes local failure of local_failure: A packets.Kind specifying what constitutes local failure of
customer work. customer work.
""" """
@ -137,21 +144,21 @@ class _TerminationManager(_interfaces.TerminationManager):
def emission_complete(self): def emission_complete(self):
"""See superclass method for specification.""" """See superclass method for specification."""
if self._outstanding_requirements is not None: if self._outstanding_requirements is not None:
self._outstanding_requirements.discard(_EMISSION) self._outstanding_requirements.discard(_Requirement.EMISSION)
if not self._outstanding_requirements: if not self._outstanding_requirements:
self._terminate(packets.Kind.COMPLETION) self._terminate(packets.Kind.COMPLETION)
def transmission_complete(self): def transmission_complete(self):
"""See superclass method for specification.""" """See superclass method for specification."""
if self._outstanding_requirements is not None: if self._outstanding_requirements is not None:
self._outstanding_requirements.discard(_TRANSMISSION) self._outstanding_requirements.discard(_Requirement.TRANSMISSION)
if not self._outstanding_requirements: if not self._outstanding_requirements:
self._terminate(packets.Kind.COMPLETION) self._terminate(packets.Kind.COMPLETION)
def ingestion_complete(self): def ingestion_complete(self):
"""See superclass method for specification.""" """See superclass method for specification."""
if self._outstanding_requirements is not None: if self._outstanding_requirements is not None:
self._outstanding_requirements.discard(_INGESTION) self._outstanding_requirements.discard(_Requirement.INGESTION)
if not self._outstanding_requirements: if not self._outstanding_requirements:
self._terminate(packets.Kind.COMPLETION) self._terminate(packets.Kind.COMPLETION)
@ -163,39 +170,46 @@ class _TerminationManager(_interfaces.TerminationManager):
self._terminate(kind) self._terminate(kind)
def front_termination_manager(work_pool, utility_pool, action, subscription): def front_termination_manager(
work_pool, utility_pool, action, subscription_kind):
"""Creates a TerminationManager appropriate for front-side use. """Creates a TerminationManager appropriate for front-side use.
Args: Args:
work_pool: A thread pool in which customer work will be done. work_pool: A thread pool in which customer work will be done.
utility_pool: A thread pool in which work utility work will be done. utility_pool: A thread pool in which work utility work will be done.
action: An action to call on operation termination. action: An action to call on operation termination.
subscription: One of interfaces.FULL, interfaces.termination_only, or subscription_kind: An interfaces.ServicedSubscription.Kind value.
interfaces.NONE.
Returns: Returns:
A TerminationManager appropriate for front-side use. A TerminationManager appropriate for front-side use.
""" """
if subscription_kind is interfaces.ServicedSubscription.Kind.NONE:
requirements = _FRONT_NOT_LISTENING_REQUIREMENTS
else:
requirements = _LISTENING_REQUIREMENTS
return _TerminationManager( return _TerminationManager(
work_pool, utility_pool, action, work_pool, utility_pool, action, requirements,
_FRONT_NOT_LISTENING_REQUIREMENTS if subscription == interfaces.NONE else packets.Kind.SERVICED_FAILURE)
_LISTENING_REQUIREMENTS, packets.Kind.SERVICED_FAILURE)
def back_termination_manager(work_pool, utility_pool, action, subscription): def back_termination_manager(work_pool, utility_pool, action, subscription_kind):
"""Creates a TerminationManager appropriate for back-side use. """Creates a TerminationManager appropriate for back-side use.
Args: Args:
work_pool: A thread pool in which customer work will be done. work_pool: A thread pool in which customer work will be done.
utility_pool: A thread pool in which work utility work will be done. utility_pool: A thread pool in which work utility work will be done.
action: An action to call on operation termination. action: An action to call on operation termination.
subscription: One of interfaces.FULL, interfaces.termination_only, or subscription_kind: An interfaces.ServicedSubscription.Kind value.
interfaces.NONE.
Returns: Returns:
A TerminationManager appropriate for back-side use. A TerminationManager appropriate for back-side use.
""" """
if subscription_kind is interfaces.ServicedSubscription.Kind.NONE:
requirements = _BACK_NOT_LISTENING_REQUIREMENTS
else:
requirements = _LISTENING_REQUIREMENTS
return _TerminationManager( return _TerminationManager(
work_pool, utility_pool, action, work_pool, utility_pool, action, requirements,
_BACK_NOT_LISTENING_REQUIREMENTS if subscription == interfaces.NONE else packets.Kind.SERVICER_FAILURE)
_LISTENING_REQUIREMENTS, packets.Kind.SERVICER_FAILURE)

@ -91,20 +91,19 @@ class _Packetizer(object):
class _FrontPacketizer(_Packetizer): class _FrontPacketizer(_Packetizer):
"""Front-side packet-creating behavior.""" """Front-side packet-creating behavior."""
def __init__(self, name, subscription, trace_id, timeout): def __init__(self, name, subscription_kind, trace_id, timeout):
"""Constructor. """Constructor.
Args: Args:
name: The name of the operation. name: The name of the operation.
subscription: One of interfaces.FULL, interfaces.TERMINATION_ONLY, or subscription_kind: An interfaces.ServicedSubscription.Kind value
interfaces.NONE describing the interest the front has in packets sent describing the interest the front has in packets sent from the back.
from the back.
trace_id: A uuid.UUID identifying a set of related operations to which trace_id: A uuid.UUID identifying a set of related operations to which
this operation belongs. this operation belongs.
timeout: A length of time in seconds to allow for the entire operation. timeout: A length of time in seconds to allow for the entire operation.
""" """
self._name = name self._name = name
self._subscription = subscription self._subscription_kind = subscription_kind
self._trace_id = trace_id self._trace_id = trace_id
self._timeout = timeout self._timeout = timeout
@ -114,13 +113,13 @@ class _FrontPacketizer(_Packetizer):
return packets.FrontToBackPacket( return packets.FrontToBackPacket(
operation_id, sequence_number, operation_id, sequence_number,
packets.Kind.COMPLETION if complete else packets.Kind.CONTINUATION, packets.Kind.COMPLETION if complete else packets.Kind.CONTINUATION,
self._name, self._subscription, self._trace_id, payload, self._name, self._subscription_kind, self._trace_id, payload,
self._timeout) self._timeout)
else: else:
return packets.FrontToBackPacket( return packets.FrontToBackPacket(
operation_id, 0, operation_id, 0,
packets.Kind.ENTIRE if complete else packets.Kind.COMMENCEMENT, packets.Kind.ENTIRE if complete else packets.Kind.COMMENCEMENT,
self._name, self._subscription, self._trace_id, payload, self._name, self._subscription_kind, self._trace_id, payload,
self._timeout) self._timeout)
def packetize_abortion(self, operation_id, sequence_number, kind): def packetize_abortion(self, operation_id, sequence_number, kind):
@ -335,8 +334,8 @@ class _TransmittingTransmissionManager(TransmissionManager):
def front_transmission_manager( def front_transmission_manager(
lock, pool, callback, operation_id, name, subscription, trace_id, timeout, lock, pool, callback, operation_id, name, subscription_kind, trace_id,
termination_manager): timeout, termination_manager):
"""Creates a TransmissionManager appropriate for front-side use. """Creates a TransmissionManager appropriate for front-side use.
Args: Args:
@ -347,9 +346,8 @@ def front_transmission_manager(
of the operation. of the operation.
operation_id: The operation's ID. operation_id: The operation's ID.
name: The name of the operation. name: The name of the operation.
subscription: One of interfaces.FULL, interfaces.TERMINATION_ONLY, or subscription_kind: An interfaces.ServicedSubscription.Kind value
interfaces.NONE describing the interest the front has in packets sent describing the interest the front has in packets sent from the back.
from the back.
trace_id: A uuid.UUID identifying a set of related operations to which trace_id: A uuid.UUID identifying a set of related operations to which
this operation belongs. this operation belongs.
timeout: A length of time in seconds to allow for the entire operation. timeout: A length of time in seconds to allow for the entire operation.
@ -361,12 +359,13 @@ def front_transmission_manager(
""" """
return _TransmittingTransmissionManager( return _TransmittingTransmissionManager(
lock, pool, callback, operation_id, _FrontPacketizer( lock, pool, callback, operation_id, _FrontPacketizer(
name, subscription, trace_id, timeout), name, subscription_kind, trace_id, timeout),
termination_manager) termination_manager)
def back_transmission_manager( def back_transmission_manager(
lock, pool, callback, operation_id, termination_manager, subscription): lock, pool, callback, operation_id, termination_manager,
subscription_kind):
"""Creates a TransmissionManager appropriate for back-side use. """Creates a TransmissionManager appropriate for back-side use.
Args: Args:
@ -378,14 +377,13 @@ def back_transmission_manager(
operation_id: The operation's ID. operation_id: The operation's ID.
termination_manager: The _interfaces.TerminationManager associated with termination_manager: The _interfaces.TerminationManager associated with
this operation. this operation.
subscription: One of interfaces.FULL, interfaces.TERMINATION_ONLY, or subscription_kind: An interfaces.ServicedSubscription.Kind value
interfaces.NONE describing the interest the front has in packets sent from describing the interest the front has in packets sent from the back.
the back.
Returns: Returns:
A TransmissionManager appropriate for back-side use. A TransmissionManager appropriate for back-side use.
""" """
if subscription == interfaces.NONE: if subscription_kind is interfaces.ServicedSubscription.Kind.NONE:
return _EmptyTransmissionManager() return _EmptyTransmissionManager()
else: else:
return _TransmittingTransmissionManager( return _TransmittingTransmissionManager(

@ -71,10 +71,9 @@ class FrontToBackPacket(
Kind.RECEPTION_FAILURE, or Kind.TRANSMISSION_FAILURE. Kind.RECEPTION_FAILURE, or Kind.TRANSMISSION_FAILURE.
name: The name of an operation. Must be present if kind is Kind.COMMENCEMENT name: The name of an operation. Must be present if kind is Kind.COMMENCEMENT
or Kind.ENTIRE. Must be None for any other kind. or Kind.ENTIRE. Must be None for any other kind.
subscription: One of interfaces.FULL, interfaces.TERMINATION_ONLY, or subscription: An interfaces.ServicedSubscription.Kind value describing the
interfaces.NONE describing the interest the front has in packets sent from interest the front has in packets sent from the back. Must be present if
the back. Must be present if kind is Kind.COMMENCEMENT or Kind.ENTIRE. kind is Kind.COMMENCEMENT or Kind.ENTIRE. Must be None for any other kind.
Must be None for any other kind.
trace_id: A uuid.UUID identifying a set of related operations to which this trace_id: A uuid.UUID identifying a set of related operations to which this
operation belongs. May be None. operation belongs. May be None.
payload: A customer payload object. Must be present if kind is payload: A customer payload object. Must be present if kind is

@ -36,13 +36,14 @@ from _framework.base import interfaces
class _ServicedSubscription( class _ServicedSubscription(
collections.namedtuple('_ServicedSubscription', ['category', 'ingestor']), collections.namedtuple('_ServicedSubscription', ['kind', 'ingestor']),
interfaces.ServicedSubscription): interfaces.ServicedSubscription):
"""See interfaces.ServicedSubscription for specification.""" """See interfaces.ServicedSubscription for specification."""
_NONE_SUBSCRIPTION = _ServicedSubscription(interfaces.NONE, None) _NONE_SUBSCRIPTION = _ServicedSubscription(
interfaces.ServicedSubscription.Kind.NONE, None)
_TERMINATION_ONLY_SUBSCRIPTION = _ServicedSubscription( _TERMINATION_ONLY_SUBSCRIPTION = _ServicedSubscription(
interfaces.TERMINATION_ONLY, None) interfaces.ServicedSubscription.Kind.TERMINATION_ONLY, None)
def none_serviced_subscription(): def none_serviced_subscription():
@ -72,12 +73,14 @@ def full_serviced_subscription(ingestor):
"""Creates a "full" interfaces.ServicedSubscription object. """Creates a "full" interfaces.ServicedSubscription object.
Args: Args:
ingestor: A ServicedIngestor. ingestor: An interfaces.ServicedIngestor.
Returns: Returns:
A ServicedSubscription object indicating a full subscription. An interfaces.ServicedSubscription object indicating a full
subscription.
""" """
return _ServicedSubscription(interfaces.FULL, ingestor) return _ServicedSubscription(
interfaces.ServicedSubscription.Kind.FULL, ingestor)
def wait_for_idle(end): def wait_for_idle(end):

@ -94,7 +94,7 @@ class _OperationCancellableIterator(interfaces.CancellableIterator):
def cancel(self): def cancel(self):
self._operation.cancel() self._operation.cancel()
self._rendezvous.set_outcome(base_interfaces.CANCELLED) self._rendezvous.set_outcome(base_interfaces.Outcome.CANCELLED)
class _OperationFuture(future.Future): class _OperationFuture(future.Future):
@ -150,15 +150,12 @@ class _OperationFuture(future.Future):
"""Indicates to this object that the operation has terminated. """Indicates to this object that the operation has terminated.
Args: Args:
operation_outcome: One of base_interfaces.COMPLETED, operation_outcome: A base_interfaces.Outcome value indicating the
base_interfaces.CANCELLED, base_interfaces.EXPIRED, outcome of the operation.
base_interfaces.RECEPTION_FAILURE, base_interfaces.TRANSMISSION_FAILURE,
base_interfaces.SERVICED_FAILURE, or base_interfaces.SERVICER_FAILURE
indicating the categorical outcome of the operation.
""" """
with self._condition: with self._condition:
if (self._outcome is None and if (self._outcome is None and
operation_outcome != base_interfaces.COMPLETED): operation_outcome is not base_interfaces.Outcome.COMPLETED):
self._outcome = future.raised( self._outcome = future.raised(
_control.abortion_outcome_to_exception(operation_outcome)) _control.abortion_outcome_to_exception(operation_outcome))
self._condition.notify_all() self._condition.notify_all()

@ -40,12 +40,16 @@ from _framework.foundation import stream
INTERNAL_ERROR_LOG_MESSAGE = ':-( RPC Framework (Face) Internal Error! :-(' INTERNAL_ERROR_LOG_MESSAGE = ':-( RPC Framework (Face) Internal Error! :-('
_OPERATION_OUTCOME_TO_RPC_ABORTION = { _OPERATION_OUTCOME_TO_RPC_ABORTION = {
base_interfaces.CANCELLED: interfaces.CANCELLED, base_interfaces.Outcome.CANCELLED: interfaces.Abortion.CANCELLED,
base_interfaces.EXPIRED: interfaces.EXPIRED, base_interfaces.Outcome.EXPIRED: interfaces.Abortion.EXPIRED,
base_interfaces.RECEPTION_FAILURE: interfaces.NETWORK_FAILURE, base_interfaces.Outcome.RECEPTION_FAILURE:
base_interfaces.TRANSMISSION_FAILURE: interfaces.NETWORK_FAILURE, interfaces.Abortion.NETWORK_FAILURE,
base_interfaces.SERVICED_FAILURE: interfaces.SERVICED_FAILURE, base_interfaces.Outcome.TRANSMISSION_FAILURE:
base_interfaces.SERVICER_FAILURE: interfaces.SERVICER_FAILURE, interfaces.Abortion.NETWORK_FAILURE,
base_interfaces.Outcome.SERVICED_FAILURE:
interfaces.Abortion.SERVICED_FAILURE,
base_interfaces.Outcome.SERVICER_FAILURE:
interfaces.Abortion.SERVICER_FAILURE,
} }
@ -59,13 +63,13 @@ def _as_operation_termination_callback(rpc_abortion_callback):
def _abortion_outcome_to_exception(abortion_outcome): def _abortion_outcome_to_exception(abortion_outcome):
if abortion_outcome == base_interfaces.CANCELLED: if abortion_outcome == base_interfaces.Outcome.CANCELLED:
return exceptions.CancellationError() return exceptions.CancellationError()
elif abortion_outcome == base_interfaces.EXPIRED: elif abortion_outcome == base_interfaces.Outcome.EXPIRED:
return exceptions.ExpirationError() return exceptions.ExpirationError()
elif abortion_outcome == base_interfaces.SERVICER_FAILURE: elif abortion_outcome == base_interfaces.Outcome.SERVICER_FAILURE:
return exceptions.ServicerError() return exceptions.ServicerError()
elif abortion_outcome == base_interfaces.SERVICED_FAILURE: elif abortion_outcome == base_interfaces.Outcome.SERVICED_FAILURE:
return exceptions.ServicedError() return exceptions.ServicedError()
else: else:
return exceptions.NetworkError() return exceptions.NetworkError()
@ -133,7 +137,7 @@ class Rendezvous(stream.Consumer):
def set_outcome(self, outcome): def set_outcome(self, outcome):
with self._condition: with self._condition:
if outcome != base_interfaces.COMPLETED: if outcome is not base_interfaces.Outcome.COMPLETED:
self._abortion = outcome self._abortion = outcome
self._condition.notify() self._condition.notify()

@ -30,6 +30,7 @@
"""Interfaces for the face layer of RPC Framework.""" """Interfaces for the face layer of RPC Framework."""
import abc import abc
import enum
# exceptions, abandonment, and future are referenced from specification in this # exceptions, abandonment, and future are referenced from specification in this
# module. # module.
@ -58,14 +59,15 @@ class CancellableIterator(object):
raise NotImplementedError() raise NotImplementedError()
# Constants that categorize RPC abortion. @enum.unique
# TODO(nathaniel): Learn and use Python's enum library for this de facto class Abortion(enum.Enum):
# enumerated type """Categories of RPC abortion."""
CANCELLED = 'abortion: cancelled'
EXPIRED = 'abortion: expired' CANCELLED = 'cancelled'
NETWORK_FAILURE = 'abortion: network failure' EXPIRED = 'expired'
SERVICED_FAILURE = 'abortion: serviced failure' NETWORK_FAILURE = 'network failure'
SERVICER_FAILURE = 'abortion: servicer failure' SERVICED_FAILURE = 'serviced failure'
SERVICER_FAILURE = 'servicer failure'
class RpcContext(object): class RpcContext(object):
@ -93,9 +95,8 @@ class RpcContext(object):
"""Registers a callback to be called if the RPC is aborted. """Registers a callback to be called if the RPC is aborted.
Args: Args:
abortion_callback: A callable to be called and passed one of CANCELLED, abortion_callback: A callable to be called and passed an Abortion value
EXPIRED, NETWORK_FAILURE, SERVICED_FAILURE, or SERVICER_FAILURE in the in the event of RPC abortion.
event of RPC abortion.
""" """
raise NotImplementedError() raise NotImplementedError()
@ -474,9 +475,8 @@ class Stub(object):
request: The request value for the RPC. request: The request value for the RPC.
response_callback: A callback to be called to accept the response value response_callback: A callback to be called to accept the response value
of the RPC. of the RPC.
abortion_callback: A callback to be called to accept one of CANCELLED, abortion_callback: A callback to be called and passed an Abortion value
EXPIRED, NETWORK_FAILURE, or SERVICER_FAILURE in the event of RPC in the event of RPC abortion.
abortion.
timeout: A duration of time in seconds to allow for the RPC. timeout: A duration of time in seconds to allow for the RPC.
Returns: Returns:
@ -494,9 +494,8 @@ class Stub(object):
request: The request value for the RPC. request: The request value for the RPC.
response_consumer: A stream.Consumer to be called to accept the response response_consumer: A stream.Consumer to be called to accept the response
values of the RPC. values of the RPC.
abortion_callback: A callback to be called to accept one of CANCELLED, abortion_callback: A callback to be called and passed an Abortion value
EXPIRED, NETWORK_FAILURE, or SERVICER_FAILURE in the event of RPC in the event of RPC abortion.
abortion.
timeout: A duration of time in seconds to allow for the RPC. timeout: A duration of time in seconds to allow for the RPC.
Returns: Returns:
@ -513,9 +512,8 @@ class Stub(object):
name: The RPC method name. name: The RPC method name.
response_callback: A callback to be called to accept the response value response_callback: A callback to be called to accept the response value
of the RPC. of the RPC.
abortion_callback: A callback to be called to accept one of CANCELLED, abortion_callback: A callback to be called and passed an Abortion value
EXPIRED, NETWORK_FAILURE, or SERVICER_FAILURE in the event of RPC in the event of RPC abortion.
abortion.
timeout: A duration of time in seconds to allow for the RPC. timeout: A duration of time in seconds to allow for the RPC.
Returns: Returns:
@ -533,9 +531,8 @@ class Stub(object):
name: The RPC method name. name: The RPC method name.
response_consumer: A stream.Consumer to be called to accept the response response_consumer: A stream.Consumer to be called to accept the response
values of the RPC. values of the RPC.
abortion_callback: A callback to be called to accept one of CANCELLED, abortion_callback: A callback to be called and passed an Abortion value
EXPIRED, NETWORK_FAILURE, or SERVICER_FAILURE in the event of RPC in the event of RPC abortion.
abortion.
timeout: A duration of time in seconds to allow for the RPC. timeout: A duration of time in seconds to allow for the RPC.
Returns: Returns:

@ -176,7 +176,7 @@ class EventInvocationSynchronousEventServiceTestCase(
name, request, callback.complete, callback.abort, _TIMEOUT) name, request, callback.complete, callback.abort, _TIMEOUT)
callback.block_until_terminated() callback.block_until_terminated()
self.assertEqual(interfaces.EXPIRED, callback.abortion()) self.assertEqual(interfaces.Abortion.EXPIRED, callback.abortion())
def testExpiredUnaryRequestStreamResponse(self): def testExpiredUnaryRequestStreamResponse(self):
for name, test_messages_sequence in ( for name, test_messages_sequence in (
@ -190,7 +190,7 @@ class EventInvocationSynchronousEventServiceTestCase(
name, request, callback, callback.abort, _TIMEOUT) name, request, callback, callback.abort, _TIMEOUT)
callback.block_until_terminated() callback.block_until_terminated()
self.assertEqual(interfaces.EXPIRED, callback.abortion()) self.assertEqual(interfaces.Abortion.EXPIRED, callback.abortion())
def testExpiredStreamRequestUnaryResponse(self): def testExpiredStreamRequestUnaryResponse(self):
for name, test_messages_sequence in ( for name, test_messages_sequence in (
@ -202,7 +202,7 @@ class EventInvocationSynchronousEventServiceTestCase(
name, callback.complete, callback.abort, _TIMEOUT) name, callback.complete, callback.abort, _TIMEOUT)
callback.block_until_terminated() callback.block_until_terminated()
self.assertEqual(interfaces.EXPIRED, callback.abortion()) self.assertEqual(interfaces.Abortion.EXPIRED, callback.abortion())
def testExpiredStreamRequestStreamResponse(self): def testExpiredStreamRequestStreamResponse(self):
for name, test_messages_sequence in ( for name, test_messages_sequence in (
@ -217,7 +217,7 @@ class EventInvocationSynchronousEventServiceTestCase(
request_consumer.consume(request) request_consumer.consume(request)
callback.block_until_terminated() callback.block_until_terminated()
self.assertEqual(interfaces.EXPIRED, callback.abortion()) self.assertEqual(interfaces.Abortion.EXPIRED, callback.abortion())
def testFailedUnaryRequestUnaryResponse(self): def testFailedUnaryRequestUnaryResponse(self):
for name, test_messages_sequence in ( for name, test_messages_sequence in (
@ -231,7 +231,7 @@ class EventInvocationSynchronousEventServiceTestCase(
name, request, callback.complete, callback.abort, _TIMEOUT) name, request, callback.complete, callback.abort, _TIMEOUT)
callback.block_until_terminated() callback.block_until_terminated()
self.assertEqual(interfaces.SERVICER_FAILURE, callback.abortion()) self.assertEqual(interfaces.Abortion.SERVICER_FAILURE, callback.abortion())
def testFailedUnaryRequestStreamResponse(self): def testFailedUnaryRequestStreamResponse(self):
for name, test_messages_sequence in ( for name, test_messages_sequence in (
@ -245,7 +245,7 @@ class EventInvocationSynchronousEventServiceTestCase(
name, request, callback, callback.abort, _TIMEOUT) name, request, callback, callback.abort, _TIMEOUT)
callback.block_until_terminated() callback.block_until_terminated()
self.assertEqual(interfaces.SERVICER_FAILURE, callback.abortion()) self.assertEqual(interfaces.Abortion.SERVICER_FAILURE, callback.abortion())
def testFailedStreamRequestUnaryResponse(self): def testFailedStreamRequestUnaryResponse(self):
for name, test_messages_sequence in ( for name, test_messages_sequence in (
@ -262,7 +262,7 @@ class EventInvocationSynchronousEventServiceTestCase(
request_consumer.terminate() request_consumer.terminate()
callback.block_until_terminated() callback.block_until_terminated()
self.assertEqual(interfaces.SERVICER_FAILURE, callback.abortion()) self.assertEqual(interfaces.Abortion.SERVICER_FAILURE, callback.abortion())
def testFailedStreamRequestStreamResponse(self): def testFailedStreamRequestStreamResponse(self):
for name, test_messages_sequence in ( for name, test_messages_sequence in (
@ -279,7 +279,7 @@ class EventInvocationSynchronousEventServiceTestCase(
request_consumer.terminate() request_consumer.terminate()
callback.block_until_terminated() callback.block_until_terminated()
self.assertEqual(interfaces.SERVICER_FAILURE, callback.abortion()) self.assertEqual(interfaces.Abortion.SERVICER_FAILURE, callback.abortion())
def testParallelInvocations(self): def testParallelInvocations(self):
for name, test_messages_sequence in ( for name, test_messages_sequence in (
@ -321,7 +321,7 @@ class EventInvocationSynchronousEventServiceTestCase(
call.cancel() call.cancel()
callback.block_until_terminated() callback.block_until_terminated()
self.assertEqual(interfaces.CANCELLED, callback.abortion()) self.assertEqual(interfaces.Abortion.CANCELLED, callback.abortion())
def testCancelledUnaryRequestStreamResponse(self): def testCancelledUnaryRequestStreamResponse(self):
for name, test_messages_sequence in ( for name, test_messages_sequence in (
@ -335,7 +335,7 @@ class EventInvocationSynchronousEventServiceTestCase(
call.cancel() call.cancel()
callback.block_until_terminated() callback.block_until_terminated()
self.assertEqual(interfaces.CANCELLED, callback.abortion()) self.assertEqual(interfaces.Abortion.CANCELLED, callback.abortion())
def testCancelledStreamRequestUnaryResponse(self): def testCancelledStreamRequestUnaryResponse(self):
for name, test_messages_sequence in ( for name, test_messages_sequence in (
@ -351,7 +351,7 @@ class EventInvocationSynchronousEventServiceTestCase(
call.cancel() call.cancel()
callback.block_until_terminated() callback.block_until_terminated()
self.assertEqual(interfaces.CANCELLED, callback.abortion()) self.assertEqual(interfaces.Abortion.CANCELLED, callback.abortion())
def testCancelledStreamRequestStreamResponse(self): def testCancelledStreamRequestStreamResponse(self):
for name, test_messages_sequence in ( for name, test_messages_sequence in (
@ -364,4 +364,4 @@ class EventInvocationSynchronousEventServiceTestCase(
call.cancel() call.cancel()
callback.block_until_terminated() callback.block_until_terminated()
self.assertEqual(interfaces.CANCELLED, callback.abortion()) self.assertEqual(interfaces.Abortion.CANCELLED, callback.abortion())

@ -35,18 +35,20 @@ namespace :spec do
t.pattern = spec_files t.pattern = spec_files
t.rspec_opts = "--tag #{suite[:tag]}" if suite[:tag] t.rspec_opts = "--tag #{suite[:tag]}" if suite[:tag]
t.rspec_opts = suite[:tags].map{ |t| "--tag #{t}" }.join(' ') if suite[:tags] if suite[:tags]
t.rspec_opts = suite[:tags].map { |x| "--tag #{x}" }.join(' ')
end
end end
end end
end end
end end
desc 'Run compiles the extension, runs all the tests' desc 'Compiles the extension then runs all the tests'
task :all task :all
task default: :all task default: :all
task 'spec:suite:wrapper' => :compile task 'spec:suite:wrapper' => [:compile, :rubocop]
task 'spec:suite:idiomatic' => 'spec:suite:wrapper' task 'spec:suite:idiomatic' => 'spec:suite:wrapper'
task 'spec:suite:bidi' => 'spec:suite:wrapper' task 'spec:suite:bidi' => 'spec:suite:wrapper'
task 'spec:suite:server' => 'spec:suite:wrapper' task 'spec:suite:server' => 'spec:suite:wrapper'
task :all => ['spec:suite:idiomatic', 'spec:suite:bidi', 'spec:suite:server'] task all: ['spec:suite:idiomatic', 'spec:suite:bidi', 'spec:suite:server']

@ -0,0 +1,44 @@
# Copyright 2014, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: google/protobuf/empty.proto
require 'google/protobuf'
Google::Protobuf::DescriptorPool.generated_pool.build do
add_message "google.protobuf.Empty" do
end
end
module Google
module Protobuf
Empty = Google::Protobuf::DescriptorPool.generated_pool.lookup("google.protobuf.Empty").msgclass
end
end

@ -0,0 +1,278 @@
#!/usr/bin/env ruby
# Copyright 2014, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# pubsub_demo demos accesses the Google PubSub API via its gRPC interface
#
# TODO: update the Usage once the usable auth gem is available
# $ SSL_CERT_FILE=<path/to/ssl/certs> \
# path/to/pubsub_demo.rb \
# --service_account_key_file=<path_to_service_account> \
# [--action=<chosen_demo_action> ]
#
# There are options related to the chosen action, see #parse_args below.
# - the possible actions are given by the method names of NamedAction class
# - the default action is list_some_topics
this_dir = File.expand_path(File.dirname(__FILE__))
lib_dir = File.join(File.dirname(File.dirname(this_dir)), 'lib')
$LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)
$LOAD_PATH.unshift(this_dir) unless $LOAD_PATH.include?(this_dir)
require 'optparse'
require 'grpc'
require 'google/protobuf'
require 'google/protobuf/empty'
require 'tech/pubsub/proto/pubsub'
require 'tech/pubsub/proto/pubsub_services'
# loads the certificates used to access the test server securely.
def load_prod_cert
fail 'could not find a production cert' if ENV['SSL_CERT_FILE'].nil?
p "loading prod certs from #{ENV['SSL_CERT_FILE']}"
File.open(ENV['SSL_CERT_FILE']).read
end
# creates a SSL Credentials from the production certificates.
def ssl_creds
GRPC::Core::Credentials.new(load_prod_cert)
end
# Builds the metadata authentication update proc.
#
# TODO: replace this once the ruby usable auth repo is available.
def auth_proc(opts)
if GRPC::Auth::GCECredentials.on_gce?
return GRPC::Auth::GCECredentials.new.updater_proc
end
fd = StringIO.new(File.read(opts.oauth_key_file))
GRPC::Auth::ServiceAccountCredentials.new(opts.oauth_scope, fd).updater_proc
end
# Creates a stub for accessing the publisher service.
def publisher_stub(opts)
address = "#{opts.host}:#{opts.port}"
stub_clz = Tech::Pubsub::PublisherService::Stub # shorter
logger.info("... access PublisherService at #{address}")
stub_clz.new(address,
creds: ssl_creds, update_metadata: auth_proc(opts),
GRPC::Core::Channel::SSL_TARGET => opts.host)
end
# Creates a stub for accessing the subscriber service.
def subscriber_stub(opts)
address = "#{opts.host}:#{opts.port}"
stub_clz = Tech::Pubsub::SubscriberService::Stub # shorter
logger.info("... access SubscriberService at #{address}")
stub_clz.new(address,
creds: ssl_creds, update_metadata: auth_proc(opts),
GRPC::Core::Channel::SSL_TARGET => opts.host)
end
# defines methods corresponding to each interop test case.
class NamedActions
include Tech::Pubsub
# Initializes NamedActions
#
# @param pub [Stub] a stub for accessing the publisher service
# @param sub [Stub] a stub for accessing the publisher service
# @param args [Args] provides access to the command line
def initialize(pub, sub, args)
@pub = pub
@sub = sub
@args = args
end
# Removes the test topic if it exists
def remove_topic
name = test_topic_name
p "... removing Topic #{name}"
@pub.delete_topic(DeleteTopicRequest.new(topic: name))
p "removed Topic: #{name} OK"
rescue GRPC::BadStatus => e
p "Could not delete a topics: rpc failed with '#{e}'"
end
# Creates a test topic
def create_topic
name = test_topic_name
p "... creating Topic #{name}"
resp = @pub.create_topic(Topic.new(name: name))
p "created Topic: #{resp.name} OK"
rescue GRPC::BadStatus => e
p "Could not create a topics: rpc failed with '#{e}'"
end
# Lists topics in the project
def list_some_topics
p 'Listing topics'
p '-------------_'
list_project_topics.topic.each { |t| p t.name }
rescue GRPC::BadStatus => e
p "Could not list topics: rpc failed with '#{e}'"
end
# Checks if a topics exists in a project
def check_exists
name = test_topic_name
p "... checking for topic #{name}"
exists = topic_exists?(name)
p "#{name} is a topic" if exists
p "#{name} is not a topic" unless exists
rescue GRPC::BadStatus => e
p "Could not check for a topics: rpc failed with '#{e}'"
end
# Publishes some messages
def random_pub_sub
topic_name, sub_name = test_topic_name, test_sub_name
create_topic_if_needed(topic_name)
@sub.create_subscription(Subscription.new(name: sub_name,
topic: topic_name))
msg_count = rand(10..30)
msg_count.times do |x|
msg = PubsubMessage.new(data: "message #{x}")
@pub.publish(PublishRequest.new(topic: topic_name, message: msg))
end
p "Sent #{msg_count} messages to #{topic_name}, checking for them now."
batch = @sub.pull_batch(PullBatchRequest.new(subscription: sub_name,
max_events: msg_count))
ack_ids = batch.pull_responses.map { |x| x.ack_id }
p "Got #{ack_ids.size} messages; acknowledging them.."
@sub.acknowledge(AcknowledgeRequest.new(subscription: sub_name,
ack_id: ack_ids))
p "Test messages were acknowledged OK, deleting the subscription"
del_req = DeleteSubscriptionRequest.new(subscription: sub_name)
@sub.delete_subscription(del_req)
rescue GRPC::BadStatus => e
p "Could not do random pub sub: rpc failed with '#{e}'"
end
private
# test_topic_name is the topic name to use in this test.
def test_topic_name
unless @args.topic_name.nil?
return "/topics/#{@args.project_id}/#{@args.topic_name}"
end
now_text = Time.now.utc.strftime('%Y%m%d%H%M%S%L')
"/topics/#{@args.project_id}/#{ENV['USER']}-#{now_text}"
end
# test_sub_name is the subscription name to use in this test.
def test_sub_name
unless @args.sub_name.nil?
return "/subscriptions/#{@args.project_id}/#{@args.sub_name}"
end
now_text = Time.now.utc.strftime('%Y%m%d%H%M%S%L')
"/subscriptions/#{@args.project_id}/#{ENV['USER']}-#{now_text}"
end
# determines if the topic name exists
def topic_exists?(name)
topics = list_project_topics.topic.map { |t| t.name }
topics.include?(name)
end
def create_topic_if_needed(name)
return if topic_exists?(name)
@pub.create_topic(Topic.new(name: name))
end
def list_project_topics
q = "cloud.googleapis.com/project in (/projects/#{@args.project_id})"
@pub.list_topics(ListTopicsRequest.new(query: q))
end
end
# Args is used to hold the command line info.
Args = Struct.new(:host, :oauth_scope, :oauth_key_file, :port, :action,
:project_id, :topic_name, :sub_name)
# validates the the command line options, returning them as an Arg.
def parse_args
args = Args.new('pubsub-staging.googleapis.com',
'https://www.googleapis.com/auth/pubsub',
nil, 443, 'list_some_topics', 'stoked-keyword-656')
OptionParser.new do |opts|
opts.on('--oauth_scope scope',
'Scope for OAuth tokens') { |v| args['oauth_scope'] = v }
opts.on('--server_host SERVER_HOST', 'server hostname') do |v|
args.host = v
end
opts.on('--server_port SERVER_PORT', 'server port') do |v|
args.port = v
end
opts.on('--service_account_key_file PATH',
'Path to the service account json key file') do |v|
args.oauth_key_file = v
end
# instance_methods(false) gives only the methods defined in that class.
scenes = NamedActions.instance_methods(false).map { |t| t.to_s }
scene_list = scenes.join(',')
opts.on("--action CODE", scenes, {}, 'pick a demo action',
" (#{scene_list})") do |v|
args.action = v
end
# Set the remaining values.
%w(project_id topic_name sub_name).each do |o|
opts.on("--#{o} VALUE", "#{o}") do |v|
args[o] = v
end
end
end.parse!
_check_args(args)
end
def _check_args(args)
%w(host port action).each do |a|
if args[a].nil?
raise OptionParser::MissingArgument.new("please specify --#{a}")
end
end
if args['oauth_key_file'].nil? || args['oauth_scope'].nil?
fail(OptionParser::MissingArgument,
'please specify both of --service_account_key_file and --oauth_scope')
end
args
end
def main
args = parse_args
pub, sub = publisher_stub(args), subscriber_stub(args)
NamedActions.new(pub, sub, args).method(args.action).call
end
main

@ -0,0 +1,174 @@
# Copyright 2014, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: tech/pubsub/proto/pubsub.proto
require 'google/protobuf'
require 'google/protobuf/empty'
Google::Protobuf::DescriptorPool.generated_pool.build do
add_message "tech.pubsub.Topic" do
optional :name, :string, 1
end
add_message "tech.pubsub.PubsubMessage" do
optional :data, :string, 1
optional :message_id, :string, 3
end
add_message "tech.pubsub.GetTopicRequest" do
optional :topic, :string, 1
end
add_message "tech.pubsub.PublishRequest" do
optional :topic, :string, 1
optional :message, :message, 2, "tech.pubsub.PubsubMessage"
end
add_message "tech.pubsub.PublishBatchRequest" do
optional :topic, :string, 1
repeated :messages, :message, 2, "tech.pubsub.PubsubMessage"
end
add_message "tech.pubsub.PublishBatchResponse" do
repeated :message_ids, :string, 1
end
add_message "tech.pubsub.ListTopicsRequest" do
optional :query, :string, 1
optional :max_results, :int32, 2
optional :page_token, :string, 3
end
add_message "tech.pubsub.ListTopicsResponse" do
repeated :topic, :message, 1, "tech.pubsub.Topic"
optional :next_page_token, :string, 2
end
add_message "tech.pubsub.DeleteTopicRequest" do
optional :topic, :string, 1
end
add_message "tech.pubsub.Subscription" do
optional :name, :string, 1
optional :topic, :string, 2
optional :query, :string, 3
optional :truncation_policy, :message, 4, "tech.pubsub.Subscription.TruncationPolicy"
optional :push_config, :message, 5, "tech.pubsub.PushConfig"
optional :ack_deadline_seconds, :int32, 6
optional :garbage_collect_seconds, :int64, 7
end
add_message "tech.pubsub.Subscription.TruncationPolicy" do
optional :max_bytes, :int64, 1
optional :max_age_seconds, :int64, 2
end
add_message "tech.pubsub.PushConfig" do
optional :push_endpoint, :string, 1
end
add_message "tech.pubsub.PubsubEvent" do
optional :subscription, :string, 1
optional :message, :message, 2, "tech.pubsub.PubsubMessage"
optional :truncated, :bool, 3
optional :deleted, :bool, 4
end
add_message "tech.pubsub.GetSubscriptionRequest" do
optional :subscription, :string, 1
end
add_message "tech.pubsub.ListSubscriptionsRequest" do
optional :query, :string, 1
optional :max_results, :int32, 3
optional :page_token, :string, 4
end
add_message "tech.pubsub.ListSubscriptionsResponse" do
repeated :subscription, :message, 1, "tech.pubsub.Subscription"
optional :next_page_token, :string, 2
end
add_message "tech.pubsub.TruncateSubscriptionRequest" do
optional :subscription, :string, 1
end
add_message "tech.pubsub.DeleteSubscriptionRequest" do
optional :subscription, :string, 1
end
add_message "tech.pubsub.ModifyPushConfigRequest" do
optional :subscription, :string, 1
optional :push_config, :message, 2, "tech.pubsub.PushConfig"
end
add_message "tech.pubsub.PullRequest" do
optional :subscription, :string, 1
optional :return_immediately, :bool, 2
end
add_message "tech.pubsub.PullResponse" do
optional :ack_id, :string, 1
optional :pubsub_event, :message, 2, "tech.pubsub.PubsubEvent"
end
add_message "tech.pubsub.PullBatchRequest" do
optional :subscription, :string, 1
optional :return_immediately, :bool, 2
optional :max_events, :int32, 3
end
add_message "tech.pubsub.PullBatchResponse" do
repeated :pull_responses, :message, 2, "tech.pubsub.PullResponse"
end
add_message "tech.pubsub.ModifyAckDeadlineRequest" do
optional :subscription, :string, 1
optional :ack_id, :string, 2
optional :ack_deadline_seconds, :int32, 3
end
add_message "tech.pubsub.AcknowledgeRequest" do
optional :subscription, :string, 1
repeated :ack_id, :string, 2
end
add_message "tech.pubsub.NackRequest" do
optional :subscription, :string, 1
repeated :ack_id, :string, 2
end
end
module Tech
module Pubsub
Topic = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.Topic").msgclass
PubsubMessage = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.PubsubMessage").msgclass
GetTopicRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.GetTopicRequest").msgclass
PublishRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.PublishRequest").msgclass
PublishBatchRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.PublishBatchRequest").msgclass
PublishBatchResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.PublishBatchResponse").msgclass
ListTopicsRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.ListTopicsRequest").msgclass
ListTopicsResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.ListTopicsResponse").msgclass
DeleteTopicRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.DeleteTopicRequest").msgclass
Subscription = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.Subscription").msgclass
Subscription::TruncationPolicy = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.Subscription.TruncationPolicy").msgclass
PushConfig = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.PushConfig").msgclass
PubsubEvent = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.PubsubEvent").msgclass
GetSubscriptionRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.GetSubscriptionRequest").msgclass
ListSubscriptionsRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.ListSubscriptionsRequest").msgclass
ListSubscriptionsResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.ListSubscriptionsResponse").msgclass
TruncateSubscriptionRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.TruncateSubscriptionRequest").msgclass
DeleteSubscriptionRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.DeleteSubscriptionRequest").msgclass
ModifyPushConfigRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.ModifyPushConfigRequest").msgclass
PullRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.PullRequest").msgclass
PullResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.PullResponse").msgclass
PullBatchRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.PullBatchRequest").msgclass
PullBatchResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.PullBatchResponse").msgclass
ModifyAckDeadlineRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.ModifyAckDeadlineRequest").msgclass
AcknowledgeRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.AcknowledgeRequest").msgclass
NackRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.NackRequest").msgclass
end
end

@ -0,0 +1,103 @@
# Copyright 2014, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# Generated by the protocol buffer compiler. DO NOT EDIT!
# Source: tech/pubsub/proto/pubsub.proto for package 'tech.pubsub'
require 'grpc'
require 'google/protobuf/empty'
require 'tech/pubsub/proto/pubsub'
module Tech
module Pubsub
module PublisherService
# TODO: add proto service documentation here
class Service
include GRPC::GenericService
self.marshal_class_method = :encode
self.unmarshal_class_method = :decode
self.service_name = 'tech.pubsub.PublisherService'
rpc :CreateTopic, Topic, Topic
rpc :Publish, PublishRequest, Google::Protobuf::Empty
rpc :PublishBatch, PublishBatchRequest, PublishBatchResponse
rpc :GetTopic, GetTopicRequest, Topic
rpc :ListTopics, ListTopicsRequest, ListTopicsResponse
rpc :DeleteTopic, DeleteTopicRequest, Google::Protobuf::Empty
end
Stub = Service.rpc_stub_class
end
module SubscriberService
# TODO: add proto service documentation here
class Service
include GRPC::GenericService
self.marshal_class_method = :encode
self.unmarshal_class_method = :decode
self.service_name = 'tech.pubsub.SubscriberService'
rpc :CreateSubscription, Subscription, Subscription
rpc :GetSubscription, GetSubscriptionRequest, Subscription
rpc :ListSubscriptions, ListSubscriptionsRequest, ListSubscriptionsResponse
rpc :DeleteSubscription, DeleteSubscriptionRequest, Google::Protobuf::Empty
rpc :TruncateSubscription, TruncateSubscriptionRequest, Google::Protobuf::Empty
rpc :ModifyPushConfig, ModifyPushConfigRequest, Google::Protobuf::Empty
rpc :Pull, PullRequest, PullResponse
rpc :PullBatch, PullBatchRequest, PullBatchResponse
rpc :ModifyAckDeadline, ModifyAckDeadlineRequest, Google::Protobuf::Empty
rpc :Acknowledge, AcknowledgeRequest, Google::Protobuf::Empty
rpc :Nack, NackRequest, Google::Protobuf::Empty
end
Stub = Service.rpc_stub_class
end
module PushEndpointService
# TODO: add proto service documentation here
class Service
include GRPC::GenericService
self.marshal_class_method = :encode
self.unmarshal_class_method = :decode
self.service_name = 'tech.pubsub.PushEndpointService'
rpc :HandlePubsubEvent, PubsubEvent, Google::Protobuf::Empty
end
Stub = Service.rpc_stub_class
end
end
end

@ -56,6 +56,8 @@ require 'test/cpp/interop/empty'
require 'signet/ssl_config' require 'signet/ssl_config'
include Google::RPC::Auth
# loads the certificates used to access the test server securely. # loads the certificates used to access the test server securely.
def load_test_certs def load_test_certs
this_dir = File.expand_path(File.dirname(__FILE__)) this_dir = File.expand_path(File.dirname(__FILE__))
@ -67,40 +69,54 @@ end
# loads the certificates used to access the test server securely. # loads the certificates used to access the test server securely.
def load_prod_cert def load_prod_cert
fail 'could not find a production cert' if ENV['SSL_CERT_FILE'].nil? fail 'could not find a production cert' if ENV['SSL_CERT_FILE'].nil?
p "loading prod certs from #{ENV['SSL_CERT_FILE']}" logger.info("loading prod certs from #{ENV['SSL_CERT_FILE']}")
File.open(ENV['SSL_CERT_FILE']).read File.open(ENV['SSL_CERT_FILE']).read
end end
# creates a Credentials from the test certificates. # creates SSL Credentials from the test certificates.
def test_creds def test_creds
certs = load_test_certs certs = load_test_certs
GRPC::Core::Credentials.new(certs[0]) GRPC::Core::Credentials.new(certs[0])
end end
RX_CERT = /-----BEGIN CERTIFICATE-----\n.*?-----END CERTIFICATE-----\n/m # creates SSL Credentials from the production certificates.
# creates a Credentials from the production certificates.
def prod_creds def prod_creds
cert_text = load_prod_cert cert_text = load_prod_cert
GRPC::Core::Credentials.new(cert_text) GRPC::Core::Credentials.new(cert_text)
end end
# creates a test stub that accesses host:port securely. # creates the SSL Credentials.
def create_stub(host, port, is_secure, host_override, use_test_ca) def ssl_creds(use_test_ca)
address = "#{host}:#{port}" return test_creds if use_test_ca
if is_secure prod_creds
creds = nil
if use_test_ca
creds = test_creds
else
creds = prod_creds
end end
# creates a test stub that accesses host:port securely.
def create_stub(opts)
address = "#{opts.host}:#{opts.port}"
if opts.secure
stub_opts = { stub_opts = {
:creds => creds, :creds => ssl_creds(opts.use_test_ca),
GRPC::Core::Channel::SSL_TARGET => host_override GRPC::Core::Channel::SSL_TARGET => opts.host_override
} }
# Add service account creds if specified
if %w(all service_account_creds).include?(opts.test_case)
unless opts.oauth_scope.nil?
fd = StringIO.new(File.read(opts.oauth_key_file))
logger.info("loading oauth certs from #{opts.oauth_key_file}")
auth_creds = ServiceAccountCredentials.new(opts.oauth_scope, fd)
stub_opts[:update_metadata] = auth_creds.updater_proc
end
end
# Add compute engine creds if specified
if %w(all compute_engine_creds).include?(opts.test_case)
unless opts.oauth_scope.nil?
stub_opts[:update_metadata] = GCECredentials.new.update_proc
end
end
logger.info("... connecting securely to #{address}") logger.info("... connecting securely to #{address}")
Grpc::Testing::TestService::Stub.new(address, **stub_opts) Grpc::Testing::TestService::Stub.new(address, **stub_opts)
else else
@ -158,9 +174,10 @@ class NamedTests
include Grpc::Testing::PayloadType include Grpc::Testing::PayloadType
attr_accessor :assertions # required by Minitest::Assertions attr_accessor :assertions # required by Minitest::Assertions
def initialize(stub) def initialize(stub, args)
@assertions = 0 # required by Minitest::Assertions @assertions = 0 # required by Minitest::Assertions
@stub = stub @stub = stub
@args = args
end end
def empty_unary def empty_unary
@ -170,21 +187,37 @@ class NamedTests
end end
def large_unary def large_unary
req_size, wanted_response_size = 271_828, 314_159 perform_large_unary
payload = Payload.new(type: :COMPRESSABLE, body: nulls(req_size))
req = SimpleRequest.new(response_type: :COMPRESSABLE,
response_size: wanted_response_size,
payload: payload)
resp = @stub.unary_call(req)
assert_equal(:COMPRESSABLE, resp.payload.type,
'large_unary: payload had the wrong type')
assert_equal(wanted_response_size, resp.payload.body.length,
'large_unary: payload had the wrong length')
assert_equal(nulls(wanted_response_size), resp.payload.body,
'large_unary: payload content is invalid')
p 'OK: large_unary' p 'OK: large_unary'
end end
def service_account_creds
# ignore this test if the oauth options are not set
if @args.oauth_scope.nil? || @args.oauth_key_file.nil?
p 'NOT RUN: service_account_creds; no service_account settings'
return
end
json_key = File.read(@args.oauth_key_file)
wanted_email = MultiJson.load(json_key)['client_email']
resp = perform_large_unary(fill_username: true,
fill_oauth_scope: true)
assert_equal(wanted_email, resp.username,
'service_account_creds: incorrect username')
assert(@args.oauth_scope.include?(resp.oauth_scope),
'service_account_creds: incorrect oauth_scope')
p 'OK: service_account_creds'
end
def compute_engine_creds
resp = perform_large_unary(fill_username: true,
fill_oauth_scope: true)
assert(@args.oauth_scope.include?(resp.oauth_scope),
'service_account_creds: incorrect oauth_scope')
assert_equal(@args.default_service_account, resp.username,
'service_account_creds: incorrect username')
p 'OK: compute_engine_creds'
end
def client_streaming def client_streaming
msg_sizes = [27_182, 8, 1828, 45_904] msg_sizes = [27_182, 8, 1828, 45_904]
wanted_aggregate_size = 74_922 wanted_aggregate_size = 74_922
@ -230,64 +263,89 @@ class NamedTests
method(m).call method(m).call
end end
end end
private
def perform_large_unary(fill_username: false, fill_oauth_scope: false)
req_size, wanted_response_size = 271_828, 314_159
payload = Payload.new(type: :COMPRESSABLE, body: nulls(req_size))
req = SimpleRequest.new(response_type: :COMPRESSABLE,
response_size: wanted_response_size,
payload: payload)
req.fill_username = fill_username
req.fill_oauth_scope = fill_oauth_scope
resp = @stub.unary_call(req)
assert_equal(:COMPRESSABLE, resp.payload.type,
'large_unary: payload had the wrong type')
assert_equal(wanted_response_size, resp.payload.body.length,
'large_unary: payload had the wrong length')
assert_equal(nulls(wanted_response_size), resp.payload.body,
'large_unary: payload content is invalid')
resp
end
end end
# Args is used to hold the command line info.
Args = Struct.new(:default_service_account, :host, :host_override,
:oauth_scope, :oauth_key_file, :port, :secure, :test_case,
:use_test_ca)
# validates the the command line options, returning them as a Hash. # validates the the command line options, returning them as a Hash.
def parse_options def parse_args
options = { args = Args.new
'secure' => false, args.host_override = 'foo.test.google.com'
'server_host' => nil,
'server_host_override' => nil,
'server_port' => nil,
'test_case' => nil
}
OptionParser.new do |opts| OptionParser.new do |opts|
opts.banner = 'Usage: --server_host <server_host> --server_port server_port' opts.on('--oauth_scope scope',
'Scope for OAuth tokens') { |v| args['oauth_scope'] = v }
opts.on('--server_host SERVER_HOST', 'server hostname') do |v| opts.on('--server_host SERVER_HOST', 'server hostname') do |v|
options['server_host'] = v args['host'] = v
end
opts.on('--default_service_account email_address',
'email address of the default service account') do |v|
args['default_service_account'] = v
end
opts.on('--service_account_key_file PATH',
'Path to the service account json key file') do |v|
args['oauth_key_file'] = v
end end
opts.on('--server_host_override HOST_OVERRIDE', opts.on('--server_host_override HOST_OVERRIDE',
'override host via a HTTP header') do |v| 'override host via a HTTP header') do |v|
options['server_host_override'] = v args['host_override'] = v
end
opts.on('--server_port SERVER_PORT', 'server port') do |v|
options['server_port'] = v
end end
opts.on('--server_port SERVER_PORT', 'server port') { |v| args['port'] = v }
# instance_methods(false) gives only the methods defined in that class # instance_methods(false) gives only the methods defined in that class
test_cases = NamedTests.instance_methods(false).map(&:to_s) test_cases = NamedTests.instance_methods(false).map(&:to_s)
test_case_list = test_cases.join(',') test_case_list = test_cases.join(',')
opts.on('--test_case CODE', test_cases, {}, 'select a test_case', opts.on('--test_case CODE', test_cases, {}, 'select a test_case',
" (#{test_case_list})") do |v| " (#{test_case_list})") { |v| args['test_case'] = v }
options['test_case'] = v
end
opts.on('-s', '--use_tls', 'require a secure connection?') do |v| opts.on('-s', '--use_tls', 'require a secure connection?') do |v|
options['secure'] = v args['secure'] = v
end end
opts.on('-t', '--use_test_ca', opts.on('-t', '--use_test_ca',
'if secure, use the test certificate?') do |v| 'if secure, use the test certificate?') do |v|
options['use_test_ca'] = v args['use_test_ca'] = v
end end
end.parse! end.parse!
_check_options(options) _check_args(args)
end end
def _check_options(opts) def _check_args(args)
%w(server_host server_port test_case).each do |arg| %w(host port test_case).each do |a|
if opts[arg].nil? if args[a].nil?
fail(OptionParser::MissingArgument, "please specify --#{arg}") fail(OptionParser::MissingArgument, "please specify --#{arg}")
end end
end end
if opts['server_host_override'].nil? if args['oauth_key_file'].nil? ^ args['oauth_scope'].nil?
opts['server_host_override'] = opts['server_host'] fail(OptionParser::MissingArgument,
'please specify both of --service_account_key_file and --oauth_scope')
end end
opts args
end end
def main def main
opts = parse_options opts = parse_args
stub = create_stub(opts['server_host'], opts['server_port'], opts['secure'], stub = create_stub(opts)
opts['server_host_override'], opts['use_test_ca']) NamedTests.new(stub, opts).method(opts['test_case']).call
NamedTests.new(stub).method(opts['test_case']).call
end end
main main

@ -41,10 +41,13 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
optional :response_type, :enum, 1, "grpc.testing.PayloadType" optional :response_type, :enum, 1, "grpc.testing.PayloadType"
optional :response_size, :int32, 2 optional :response_size, :int32, 2
optional :payload, :message, 3, "grpc.testing.Payload" optional :payload, :message, 3, "grpc.testing.Payload"
optional :fill_username, :bool, 4
optional :fill_oauth_scope, :bool, 5
end end
add_message "grpc.testing.SimpleResponse" do add_message "grpc.testing.SimpleResponse" do
optional :payload, :message, 1, "grpc.testing.Payload" optional :payload, :message, 1, "grpc.testing.Payload"
optional :effective_gaia_user_id, :int64, 2 optional :username, :string, 2
optional :oauth_scope, :string, 3
end end
add_message "grpc.testing.StreamingInputCallRequest" do add_message "grpc.testing.StreamingInputCallRequest" do
optional :payload, :message, 1, "grpc.testing.Payload" optional :payload, :message, 1, "grpc.testing.Payload"

@ -125,7 +125,7 @@ int grpc_rb_call_add_metadata_hash_cb(VALUE key, VALUE val, VALUE call_obj) {
md_obj_args[1] = rb_ary_entry(val, i); md_obj_args[1] = rb_ary_entry(val, i);
md_obj = rb_class_new_instance(2, md_obj_args, rb_cMetadata); md_obj = rb_class_new_instance(2, md_obj_args, rb_cMetadata);
md = grpc_rb_get_wrapped_metadata(md_obj); md = grpc_rb_get_wrapped_metadata(md_obj);
err = grpc_call_add_metadata(call, md, NUM2UINT(flags)); err = grpc_call_add_metadata_old(call, md, NUM2UINT(flags));
if (err != GRPC_CALL_OK) { if (err != GRPC_CALL_OK) {
rb_raise(rb_eCallError, "add metadata failed: %s (code=%d)", rb_raise(rb_eCallError, "add metadata failed: %s (code=%d)",
grpc_call_error_detail_of(err), err); grpc_call_error_detail_of(err), err);
@ -136,7 +136,7 @@ int grpc_rb_call_add_metadata_hash_cb(VALUE key, VALUE val, VALUE call_obj) {
md_obj_args[1] = val; md_obj_args[1] = val;
md_obj = rb_class_new_instance(2, md_obj_args, rb_cMetadata); md_obj = rb_class_new_instance(2, md_obj_args, rb_cMetadata);
md = grpc_rb_get_wrapped_metadata(md_obj); md = grpc_rb_get_wrapped_metadata(md_obj);
err = grpc_call_add_metadata(call, md, NUM2UINT(flags)); err = grpc_call_add_metadata_old(call, md, NUM2UINT(flags));
if (err != GRPC_CALL_OK) { if (err != GRPC_CALL_OK) {
rb_raise(rb_eCallError, "add metadata failed: %s (code=%d)", rb_raise(rb_eCallError, "add metadata failed: %s (code=%d)",
grpc_call_error_detail_of(err), err); grpc_call_error_detail_of(err), err);
@ -220,7 +220,7 @@ static VALUE grpc_rb_call_invoke(int argc, VALUE *argv, VALUE self) {
} }
cq = grpc_rb_get_wrapped_completion_queue(cqueue); cq = grpc_rb_get_wrapped_completion_queue(cqueue);
Data_Get_Struct(self, grpc_call, call); Data_Get_Struct(self, grpc_call, call);
err = grpc_call_invoke(call, cq, ROBJECT(metadata_read_tag), err = grpc_call_invoke_old(call, cq, ROBJECT(metadata_read_tag),
ROBJECT(finished_tag), NUM2UINT(flags)); ROBJECT(finished_tag), NUM2UINT(flags));
if (err != GRPC_CALL_OK) { if (err != GRPC_CALL_OK) {
rb_raise(rb_eCallError, "invoke failed: %s (code=%d)", rb_raise(rb_eCallError, "invoke failed: %s (code=%d)",
@ -242,7 +242,7 @@ static VALUE grpc_rb_call_start_read(VALUE self, VALUE tag) {
grpc_call *call = NULL; grpc_call *call = NULL;
grpc_call_error err; grpc_call_error err;
Data_Get_Struct(self, grpc_call, call); Data_Get_Struct(self, grpc_call, call);
err = grpc_call_start_read(call, ROBJECT(tag)); err = grpc_call_start_read_old(call, ROBJECT(tag));
if (err != GRPC_CALL_OK) { if (err != GRPC_CALL_OK) {
rb_raise(rb_eCallError, "start read failed: %s (code=%d)", rb_raise(rb_eCallError, "start read failed: %s (code=%d)",
grpc_call_error_detail_of(err), err); grpc_call_error_detail_of(err), err);
@ -330,7 +330,7 @@ static VALUE grpc_rb_call_start_write(int argc, VALUE *argv, VALUE self) {
} }
bfr = grpc_rb_get_wrapped_byte_buffer(byte_buffer); bfr = grpc_rb_get_wrapped_byte_buffer(byte_buffer);
Data_Get_Struct(self, grpc_call, call); Data_Get_Struct(self, grpc_call, call);
err = grpc_call_start_write(call, bfr, ROBJECT(tag), NUM2UINT(flags)); err = grpc_call_start_write_old(call, bfr, ROBJECT(tag), NUM2UINT(flags));
if (err != GRPC_CALL_OK) { if (err != GRPC_CALL_OK) {
rb_raise(rb_eCallError, "start write failed: %s (code=%d)", rb_raise(rb_eCallError, "start write failed: %s (code=%d)",
grpc_call_error_detail_of(err), err); grpc_call_error_detail_of(err), err);
@ -358,7 +358,7 @@ static VALUE grpc_rb_call_start_write_status(VALUE self, VALUE code,
grpc_call *call = NULL; grpc_call *call = NULL;
grpc_call_error err; grpc_call_error err;
Data_Get_Struct(self, grpc_call, call); Data_Get_Struct(self, grpc_call, call);
err = grpc_call_start_write_status(call, NUM2UINT(code), err = grpc_call_start_write_status_old(call, NUM2UINT(code),
StringValueCStr(status), ROBJECT(tag)); StringValueCStr(status), ROBJECT(tag));
if (err != GRPC_CALL_OK) { if (err != GRPC_CALL_OK) {
rb_raise(rb_eCallError, "start write status: %s (code=%d)", rb_raise(rb_eCallError, "start write status: %s (code=%d)",
@ -374,7 +374,7 @@ static VALUE grpc_rb_call_writes_done(VALUE self, VALUE tag) {
grpc_call *call = NULL; grpc_call *call = NULL;
grpc_call_error err; grpc_call_error err;
Data_Get_Struct(self, grpc_call, call); Data_Get_Struct(self, grpc_call, call);
err = grpc_call_writes_done(call, ROBJECT(tag)); err = grpc_call_writes_done_old(call, ROBJECT(tag));
if (err != GRPC_CALL_OK) { if (err != GRPC_CALL_OK) {
rb_raise(rb_eCallError, "writes done: %s (code=%d)", rb_raise(rb_eCallError, "writes done: %s (code=%d)",
grpc_call_error_detail_of(err), err); grpc_call_error_detail_of(err), err);
@ -405,7 +405,7 @@ static VALUE grpc_rb_call_server_end_initial_metadata(int argc, VALUE *argv,
flags = UINT2NUM(0); /* Default to no flags */ flags = UINT2NUM(0); /* Default to no flags */
} }
Data_Get_Struct(self, grpc_call, call); Data_Get_Struct(self, grpc_call, call);
err = grpc_call_server_end_initial_metadata(call, NUM2UINT(flags)); err = grpc_call_server_end_initial_metadata_old(call, NUM2UINT(flags));
if (err != GRPC_CALL_OK) { if (err != GRPC_CALL_OK) {
rb_raise(rb_eCallError, "end_initial_metadata failed: %s (code=%d)", rb_raise(rb_eCallError, "end_initial_metadata failed: %s (code=%d)",
grpc_call_error_detail_of(err), err); grpc_call_error_detail_of(err), err);
@ -430,7 +430,7 @@ static VALUE grpc_rb_call_server_accept(VALUE self, VALUE cqueue,
grpc_completion_queue *cq = grpc_rb_get_wrapped_completion_queue(cqueue); grpc_completion_queue *cq = grpc_rb_get_wrapped_completion_queue(cqueue);
grpc_call_error err; grpc_call_error err;
Data_Get_Struct(self, grpc_call, call); Data_Get_Struct(self, grpc_call, call);
err = grpc_call_server_accept(call, cq, ROBJECT(finished_tag)); err = grpc_call_server_accept_old(call, cq, ROBJECT(finished_tag));
if (err != GRPC_CALL_OK) { if (err != GRPC_CALL_OK) {
rb_raise(rb_eCallError, "server_accept failed: %s (code=%d)", rb_raise(rb_eCallError, "server_accept failed: %s (code=%d)",
grpc_call_error_detail_of(err), err); grpc_call_error_detail_of(err), err);

@ -192,7 +192,8 @@ static VALUE grpc_rb_channel_create_call(VALUE self, VALUE method, VALUE host,
rb_raise(rb_eRuntimeError, "closed!"); rb_raise(rb_eRuntimeError, "closed!");
} }
call = grpc_channel_create_call(ch, method_chars, host_chars, call =
grpc_channel_create_call_old(ch, method_chars, host_chars,
grpc_rb_time_timeval(deadline, grpc_rb_time_timeval(deadline,
/* absolute time */ 0)); /* absolute time */ 0));
if (call == NULL) { if (call == NULL) {

@ -175,7 +175,7 @@ static VALUE grpc_rb_server_request_call(VALUE self, VALUE tag_new) {
if (s->wrapped == NULL) { if (s->wrapped == NULL) {
rb_raise(rb_eRuntimeError, "closed!"); rb_raise(rb_eRuntimeError, "closed!");
} else { } else {
err = grpc_server_request_call(s->wrapped, ROBJECT(tag_new)); err = grpc_server_request_call_old(s->wrapped, ROBJECT(tag_new));
if (err != GRPC_CALL_OK) { if (err != GRPC_CALL_OK) {
rb_raise(rb_eCallError, "server request failed: %s (code=%d)", rb_raise(rb_eCallError, "server request failed: %s (code=%d)",
grpc_call_error_detail_of(err), err); grpc_call_error_detail_of(err), err);

@ -1,3 +1,4 @@
# -*- ruby -*-
# encoding: utf-8 # encoding: utf-8
$LOAD_PATH.push File.expand_path('../lib', __FILE__) $LOAD_PATH.push File.expand_path('../lib', __FILE__)
require 'grpc/version' require 'grpc/version'
@ -19,11 +20,14 @@ Gem::Specification.new do |s|
s.require_paths = ['lib'] s.require_paths = ['lib']
s.platform = Gem::Platform::RUBY s.platform = Gem::Platform::RUBY
s.add_dependency 'xray' s.add_dependency 'faraday', '~> 0.9'
s.add_dependency 'logging', '~> 1.8'
s.add_dependency 'google-protobuf', '~> 3.0.0alpha.1.1' s.add_dependency 'google-protobuf', '~> 3.0.0alpha.1.1'
s.add_dependency 'signet', '~> 0.5.1' s.add_dependency 'logging', '~> 1.8'
s.add_dependency 'jwt', '~> 1.2.1'
s.add_dependency 'minitest', '~> 5.4' # reqd for interop tests s.add_dependency 'minitest', '~> 5.4' # reqd for interop tests
s.add_dependency 'multi_json', '1.10.1'
s.add_dependency 'signet', '~> 0.6.0'
s.add_dependency 'xray', '~> 1.1'
s.add_development_dependency 'bundler', '~> 1.7' s.add_development_dependency 'bundler', '~> 1.7'
s.add_development_dependency 'rake', '~> 10.0' s.add_development_dependency 'rake', '~> 10.0'

@ -27,6 +27,8 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
require 'grpc/auth/compute_engine.rb'
require 'grpc/auth/service_account.rb'
require 'grpc/errors' require 'grpc/errors'
require 'grpc/grpc' require 'grpc/grpc'
require 'grpc/logconfig' require 'grpc/logconfig'

@ -0,0 +1,69 @@
# 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.
require 'faraday'
require 'grpc/auth/signet'
module Google
module RPC
# Module Auth provides classes that provide Google-specific authentication
# used to access Google gRPC services.
module Auth
# Extends Signet::OAuth2::Client so that the auth token is obtained from
# the GCE metadata server.
class GCECredentials < Signet::OAuth2::Client
COMPUTE_AUTH_TOKEN_URI = 'http://metadata/computeMetadata/v1/'\
'instance/service-accounts/default/token'
COMPUTE_CHECK_URI = 'http://metadata.google.internal'
# Detect if this appear to be a GCE instance, by checking if metadata
# is available
def self.on_gce?(options = {})
c = options[:connection] || Faraday.default_connection
resp = c.get(COMPUTE_CHECK_URI)
return false unless resp.status == 200
return false unless resp.headers.key?('Metadata-Flavor')
return resp.headers['Metadata-Flavor'] == 'Google'
rescue Faraday::ConnectionFailed
return false
end
# Overrides the super class method to change how access tokens are
# fetched.
def fetch_access_token(options = {})
c = options[:connection] || Faraday.default_connection
c.headers = { 'Metadata-Flavor' => 'Google' }
resp = c.get(COMPUTE_AUTH_TOKEN_URI)
Signet::OAuth2.parse_credentials(resp.body,
resp.headers['content-type'])
end
end
end
end
end

@ -0,0 +1,68 @@
# 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.
require 'grpc/auth/signet'
require 'multi_json'
require 'openssl'
# Reads the private key and client email fields from service account JSON key.
def read_json_key(json_key_io)
json_key = MultiJson.load(json_key_io.read)
fail 'missing client_email' unless json_key.key?('client_email')
fail 'missing private_key' unless json_key.key?('private_key')
[json_key['private_key'], json_key['client_email']]
end
module Google
module RPC
# Module Auth provides classes that provide Google-specific authentication
# used to access Google gRPC services.
module Auth
# Authenticates requests using Google's Service Account credentials.
# (cf https://developers.google.com/accounts/docs/OAuth2ServiceAccount)
class ServiceAccountCredentials < Signet::OAuth2::Client
TOKEN_CRED_URI = 'https://www.googleapis.com/oauth2/v3/token'
AUDIENCE = TOKEN_CRED_URI
# Initializes a ServiceAccountCredentials.
#
# @param scope [string|array] the scope(s) to access
# @param json_key_io [IO] an IO from which the JSON key can be read
def initialize(scope, json_key_io)
private_key, client_email = read_json_key(json_key_io)
super(token_credential_uri: TOKEN_CRED_URI,
audience: AUDIENCE,
scope: scope,
issuer: client_email,
signing_key: OpenSSL::PKey::RSA.new(private_key))
end
end
end
end
end

@ -0,0 +1,67 @@
# 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.
require 'signet/oauth_2/client'
module Signet
# Signet::OAuth2 supports OAuth2 authentication.
module OAuth2
AUTH_METADATA_KEY = :Authorization
# Signet::OAuth2::Client creates an OAuth2 client
#
# Here client is re-opened to add the #apply and #apply! methods which
# update a hash map with the fetched authentication token
#
# Eventually, this change may be merged into signet itself, or some other
# package that provides Google-specific auth via signet, and this extension
# will be unnecessary.
class Client
# Updates a_hash updated with the authentication token
def apply!(a_hash, opts = {})
# fetch the access token there is currently not one, or if the client
# has expired
fetch_access_token!(opts) if access_token.nil? || expired?
a_hash[AUTH_METADATA_KEY] = "Bearer #{access_token}"
end
# Returns a clone of a_hash updated with the authentication token
def apply(a_hash, opts = {})
a_copy = a_hash.clone
apply!(a_copy, opts)
a_copy
end
# Returns a reference to the #apply method, suitable for passing as
# a closure
def updater_proc
lambda(&method(:apply))
end
end
end
end

@ -0,0 +1,163 @@
# 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.
spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
$LOAD_PATH.unshift(spec_dir)
$LOAD_PATH.uniq!
require 'faraday'
require 'spec_helper'
def build_json_response(payload)
[200,
{ 'Content-Type' => 'application/json; charset=utf-8' },
MultiJson.dump(payload)]
end
WANTED_AUTH_KEY = :Authorization
shared_examples 'apply/apply! are OK' do
# tests that use these examples need to define
#
# @client which should be an auth client
#
# @make_auth_stubs, which should stub out the expected http behaviour of the
# auth client
describe '#fetch_access_token' do
it 'should set access_token to the fetched value' do
token = '1/abcdef1234567890'
stubs = make_auth_stubs with_access_token: token
c = Faraday.new do |b|
b.adapter(:test, stubs)
end
@client.fetch_access_token!(connection: c)
expect(@client.access_token).to eq(token)
stubs.verify_stubbed_calls
end
end
describe '#apply!' do
it 'should update the target hash with fetched access token' do
token = '1/abcdef1234567890'
stubs = make_auth_stubs with_access_token: token
c = Faraday.new do |b|
b.adapter(:test, stubs)
end
md = { foo: 'bar' }
@client.apply!(md, connection: c)
want = { :foo => 'bar', WANTED_AUTH_KEY => "Bearer #{token}" }
expect(md).to eq(want)
stubs.verify_stubbed_calls
end
end
describe 'updater_proc' do
it 'should provide a proc that updates a hash with the access token' do
token = '1/abcdef1234567890'
stubs = make_auth_stubs with_access_token: token
c = Faraday.new do |b|
b.adapter(:test, stubs)
end
md = { foo: 'bar' }
the_proc = @client.updater_proc
got = the_proc.call(md, connection: c)
want = { :foo => 'bar', WANTED_AUTH_KEY => "Bearer #{token}" }
expect(got).to eq(want)
stubs.verify_stubbed_calls
end
end
describe '#apply' do
it 'should not update the original hash with the access token' do
token = '1/abcdef1234567890'
stubs = make_auth_stubs with_access_token: token
c = Faraday.new do |b|
b.adapter(:test, stubs)
end
md = { foo: 'bar' }
@client.apply(md, connection: c)
want = { foo: 'bar' }
expect(md).to eq(want)
stubs.verify_stubbed_calls
end
it 'should add the token to the returned hash' do
token = '1/abcdef1234567890'
stubs = make_auth_stubs with_access_token: token
c = Faraday.new do |b|
b.adapter(:test, stubs)
end
md = { foo: 'bar' }
got = @client.apply(md, connection: c)
want = { :foo => 'bar', WANTED_AUTH_KEY => "Bearer #{token}" }
expect(got).to eq(want)
stubs.verify_stubbed_calls
end
it 'should not fetch a new token if the current is not expired' do
token = '1/abcdef1234567890'
stubs = make_auth_stubs with_access_token: token
c = Faraday.new do |b|
b.adapter(:test, stubs)
end
n = 5 # arbitrary
n.times do |_t|
md = { foo: 'bar' }
got = @client.apply(md, connection: c)
want = { :foo => 'bar', WANTED_AUTH_KEY => "Bearer #{token}" }
expect(got).to eq(want)
end
stubs.verify_stubbed_calls
end
it 'should fetch a new token if the current one is expired' do
token_1 = '1/abcdef1234567890'
token_2 = '2/abcdef1234567890'
[token_1, token_2].each do |t|
stubs = make_auth_stubs with_access_token: t
c = Faraday.new do |b|
b.adapter(:test, stubs)
end
md = { foo: 'bar' }
got = @client.apply(md, connection: c)
want = { :foo => 'bar', WANTED_AUTH_KEY => "Bearer #{t}" }
expect(got).to eq(want)
stubs.verify_stubbed_calls
@client.expires_at -= 3601 # default is to expire in 1hr
end
end
end
end

@ -0,0 +1,108 @@
# 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.
spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
$LOAD_PATH.unshift(spec_dir)
$LOAD_PATH.uniq!
require 'apply_auth_examples'
require 'faraday'
require 'grpc/auth/compute_engine'
require 'spec_helper'
describe Google::RPC::Auth::GCECredentials do
MD_URI = '/computeMetadata/v1/instance/service-accounts/default/token'
GCECredentials = Google::RPC::Auth::GCECredentials
before(:example) do
@client = GCECredentials.new
end
def make_auth_stubs(with_access_token: '')
Faraday::Adapter::Test::Stubs.new do |stub|
stub.get(MD_URI) do |env|
headers = env[:request_headers]
expect(headers['Metadata-Flavor']).to eq('Google')
build_json_response(
'access_token' => with_access_token,
'token_type' => 'Bearer',
'expires_in' => 3600)
end
end
end
it_behaves_like 'apply/apply! are OK'
describe '#on_gce?' do
it 'should be true when Metadata-Flavor is Google' do
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
stub.get('/') do |_env|
[200,
{ 'Metadata-Flavor' => 'Google' },
'']
end
end
c = Faraday.new do |b|
b.adapter(:test, stubs)
end
expect(GCECredentials.on_gce?(connection: c)).to eq(true)
stubs.verify_stubbed_calls
end
it 'should be false when Metadata-Flavor is not Google' do
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
stub.get('/') do |_env|
[200,
{ 'Metadata-Flavor' => 'NotGoogle' },
'']
end
end
c = Faraday.new do |b|
b.adapter(:test, stubs)
end
expect(GCECredentials.on_gce?(connection: c)).to eq(false)
stubs.verify_stubbed_calls
end
it 'should be false if the response is not 200' do
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
stub.get('/') do |_env|
[404,
{ 'Metadata-Flavor' => 'Google' },
'']
end
end
c = Faraday.new do |b|
b.adapter(:test, stubs)
end
expect(GCECredentials.on_gce?(connection: c)).to eq(false)
stubs.verify_stubbed_calls
end
end
end

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

Loading…
Cancel
Save