grpc 0.5.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of grpc might be problematic. Click here for more details.

Files changed (86) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.rspec +1 -0
  4. data/.rubocop.yml +10 -0
  5. data/.rubocop_todo.yml +52 -0
  6. data/Gemfile +4 -0
  7. data/README.md +82 -0
  8. data/Rakefile +54 -0
  9. data/bin/apis/google/protobuf/empty.rb +44 -0
  10. data/bin/apis/pubsub_demo.rb +267 -0
  11. data/bin/apis/tech/pubsub/proto/pubsub.rb +174 -0
  12. data/bin/apis/tech/pubsub/proto/pubsub_services.rb +103 -0
  13. data/bin/interop/README.md +8 -0
  14. data/bin/interop/interop_client.rb +334 -0
  15. data/bin/interop/interop_server.rb +192 -0
  16. data/bin/interop/test/cpp/interop/empty.rb +44 -0
  17. data/bin/interop/test/cpp/interop/messages.rb +89 -0
  18. data/bin/interop/test/cpp/interop/test.rb +43 -0
  19. data/bin/interop/test/cpp/interop/test_services.rb +60 -0
  20. data/bin/math.proto +80 -0
  21. data/bin/math.rb +61 -0
  22. data/bin/math_client.rb +147 -0
  23. data/bin/math_server.rb +190 -0
  24. data/bin/math_services.rb +56 -0
  25. data/bin/noproto_client.rb +108 -0
  26. data/bin/noproto_server.rb +112 -0
  27. data/ext/grpc/extconf.rb +76 -0
  28. data/ext/grpc/rb_byte_buffer.c +241 -0
  29. data/ext/grpc/rb_byte_buffer.h +54 -0
  30. data/ext/grpc/rb_call.c +569 -0
  31. data/ext/grpc/rb_call.h +59 -0
  32. data/ext/grpc/rb_channel.c +264 -0
  33. data/ext/grpc/rb_channel.h +49 -0
  34. data/ext/grpc/rb_channel_args.c +154 -0
  35. data/ext/grpc/rb_channel_args.h +52 -0
  36. data/ext/grpc/rb_completion_queue.c +185 -0
  37. data/ext/grpc/rb_completion_queue.h +50 -0
  38. data/ext/grpc/rb_credentials.c +281 -0
  39. data/ext/grpc/rb_credentials.h +50 -0
  40. data/ext/grpc/rb_event.c +361 -0
  41. data/ext/grpc/rb_event.h +53 -0
  42. data/ext/grpc/rb_grpc.c +274 -0
  43. data/ext/grpc/rb_grpc.h +74 -0
  44. data/ext/grpc/rb_metadata.c +215 -0
  45. data/ext/grpc/rb_metadata.h +53 -0
  46. data/ext/grpc/rb_server.c +278 -0
  47. data/ext/grpc/rb_server.h +50 -0
  48. data/ext/grpc/rb_server_credentials.c +210 -0
  49. data/ext/grpc/rb_server_credentials.h +50 -0
  50. data/grpc.gemspec +41 -0
  51. data/lib/grpc.rb +39 -0
  52. data/lib/grpc/core/event.rb +44 -0
  53. data/lib/grpc/core/time_consts.rb +71 -0
  54. data/lib/grpc/errors.rb +61 -0
  55. data/lib/grpc/generic/active_call.rb +536 -0
  56. data/lib/grpc/generic/bidi_call.rb +221 -0
  57. data/lib/grpc/generic/client_stub.rb +413 -0
  58. data/lib/grpc/generic/rpc_desc.rb +150 -0
  59. data/lib/grpc/generic/rpc_server.rb +404 -0
  60. data/lib/grpc/generic/service.rb +235 -0
  61. data/lib/grpc/logconfig.rb +40 -0
  62. data/lib/grpc/version.rb +33 -0
  63. data/spec/alloc_spec.rb +44 -0
  64. data/spec/byte_buffer_spec.rb +67 -0
  65. data/spec/call_spec.rb +163 -0
  66. data/spec/channel_spec.rb +181 -0
  67. data/spec/client_server_spec.rb +372 -0
  68. data/spec/completion_queue_spec.rb +74 -0
  69. data/spec/credentials_spec.rb +71 -0
  70. data/spec/event_spec.rb +53 -0
  71. data/spec/generic/active_call_spec.rb +373 -0
  72. data/spec/generic/client_stub_spec.rb +519 -0
  73. data/spec/generic/rpc_desc_spec.rb +357 -0
  74. data/spec/generic/rpc_server_pool_spec.rb +139 -0
  75. data/spec/generic/rpc_server_spec.rb +404 -0
  76. data/spec/generic/service_spec.rb +342 -0
  77. data/spec/metadata_spec.rb +64 -0
  78. data/spec/server_credentials_spec.rb +69 -0
  79. data/spec/server_spec.rb +212 -0
  80. data/spec/spec_helper.rb +51 -0
  81. data/spec/testdata/README +1 -0
  82. data/spec/testdata/ca.pem +15 -0
  83. data/spec/testdata/server1.key +16 -0
  84. data/spec/testdata/server1.pem +16 -0
  85. data/spec/time_consts_spec.rb +89 -0
  86. metadata +353 -0
@@ -0,0 +1,50 @@
1
+ /*
2
+ *
3
+ * Copyright 2015, Google Inc.
4
+ * All rights reserved.
5
+ *
6
+ * Redistribution and use in source and binary forms, with or without
7
+ * modification, are permitted provided that the following conditions are
8
+ * met:
9
+ *
10
+ * * Redistributions of source code must retain the above copyright
11
+ * notice, this list of conditions and the following disclaimer.
12
+ * * Redistributions in binary form must reproduce the above
13
+ * copyright notice, this list of conditions and the following disclaimer
14
+ * in the documentation and/or other materials provided with the
15
+ * distribution.
16
+ * * Neither the name of Google Inc. nor the names of its
17
+ * contributors may be used to endorse or promote products derived from
18
+ * this software without specific prior written permission.
19
+ *
20
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
+ *
32
+ */
33
+
34
+ #ifndef GRPC_RB_SERVER_CREDENTIALS_H_
35
+ #define GRPC_RB_SERVER_CREDENTIALS_H_
36
+
37
+ #include <ruby.h>
38
+ #include <grpc/grpc_security.h>
39
+
40
+ /* rb_cServerCredentials is the ruby class whose instances proxy
41
+ grpc_server_credentials. */
42
+ extern VALUE rb_cServerCredentials;
43
+
44
+ /* Initializes the ruby ServerCredentials class. */
45
+ void Init_grpc_server_credentials();
46
+
47
+ /* Gets the wrapped server_credentials from the ruby wrapper */
48
+ grpc_server_credentials* grpc_rb_get_wrapped_server_credentials(VALUE v);
49
+
50
+ #endif /* GRPC_RB_SERVER_CREDENTIALS_H_ */
@@ -0,0 +1,41 @@
1
+ # -*- ruby -*-
2
+ # encoding: utf-8
3
+ $LOAD_PATH.push File.expand_path('../lib', __FILE__)
4
+ require 'grpc/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = 'grpc'
8
+ s.version = GRPC::VERSION
9
+ s.authors = ['gRPC Authors']
10
+ s.email = 'temiola@google.com'
11
+ s.homepage = 'https://github.com/google/grpc/tree/master/src/ruby'
12
+ s.summary = 'GRPC system in Ruby'
13
+ s.description = 'Send RPCs from Ruby using GRPC'
14
+ s.license = 'BSD-3-Clause'
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- spec/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*.rb`.split("\n").map do |f|
19
+ File.basename(f)
20
+ end
21
+ s.require_paths = ['lib']
22
+ s.platform = Gem::Platform::RUBY
23
+
24
+ s.add_dependency 'faraday', '~> 0.9'
25
+ s.add_dependency 'google-protobuf', '~> 3.0.0alpha.1.1'
26
+ s.add_dependency 'googleauth', '~> 0.1'
27
+ s.add_dependency 'logging', '~> 1.8'
28
+ s.add_dependency 'jwt', '~> 1.2.1'
29
+ s.add_dependency 'minitest', '~> 5.4' # reqd for interop tests
30
+ s.add_dependency 'multi_json', '1.10.1'
31
+ s.add_dependency 'signet', '~> 0.6.0'
32
+ s.add_dependency 'xray', '~> 1.1'
33
+
34
+ s.add_development_dependency 'bundler', '~> 1.7'
35
+ s.add_development_dependency 'rake', '~> 10.0'
36
+ s.add_development_dependency 'rake-compiler', '~> 0'
37
+ s.add_development_dependency 'rubocop', '~> 0.28.0'
38
+ s.add_development_dependency 'rspec', '~> 3.0'
39
+
40
+ s.extensions = %w(ext/grpc/extconf.rb)
41
+ end
@@ -0,0 +1,39 @@
1
+ # Copyright 2015, Google Inc.
2
+ # All rights reserved.
3
+ #
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are
6
+ # met:
7
+ #
8
+ # * Redistributions of source code must retain the above copyright
9
+ # notice, this list of conditions and the following disclaimer.
10
+ # * Redistributions in binary form must reproduce the above
11
+ # copyright notice, this list of conditions and the following disclaimer
12
+ # in the documentation and/or other materials provided with the
13
+ # distribution.
14
+ # * Neither the name of Google Inc. nor the names of its
15
+ # contributors may be used to endorse or promote products derived from
16
+ # this software without specific prior written permission.
17
+ #
18
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22
+ # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
+ # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+
30
+ require 'grpc/errors'
31
+ require 'grpc/grpc'
32
+ require 'grpc/logconfig'
33
+ require 'grpc/version'
34
+ require 'grpc/core/event'
35
+ require 'grpc/core/time_consts'
36
+ require 'grpc/generic/active_call'
37
+ require 'grpc/generic/client_stub'
38
+ require 'grpc/generic/service'
39
+ require 'grpc/generic/rpc_server'
@@ -0,0 +1,44 @@
1
+ # Copyright 2015, Google Inc.
2
+ # All rights reserved.
3
+ #
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are
6
+ # met:
7
+ #
8
+ # * Redistributions of source code must retain the above copyright
9
+ # notice, this list of conditions and the following disclaimer.
10
+ # * Redistributions in binary form must reproduce the above
11
+ # copyright notice, this list of conditions and the following disclaimer
12
+ # in the documentation and/or other materials provided with the
13
+ # distribution.
14
+ # * Neither the name of Google Inc. nor the names of its
15
+ # contributors may be used to endorse or promote products derived from
16
+ # this software without specific prior written permission.
17
+ #
18
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22
+ # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
+ # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+
30
+ require 'grpc'
31
+
32
+ # GRPC contains the General RPC module.
33
+ module GRPC
34
+ module Core
35
+ # Event is a class defined in the c extension
36
+ #
37
+ # Here, we add an inspect method.
38
+ class Event
39
+ def inspect
40
+ "<#{self.class}: type:#{type}, tag:#{tag} result:#{result}>"
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,71 @@
1
+ # Copyright 2015, Google Inc.
2
+ # All rights reserved.
3
+ #
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are
6
+ # met:
7
+ #
8
+ # * Redistributions of source code must retain the above copyright
9
+ # notice, this list of conditions and the following disclaimer.
10
+ # * Redistributions in binary form must reproduce the above
11
+ # copyright notice, this list of conditions and the following disclaimer
12
+ # in the documentation and/or other materials provided with the
13
+ # distribution.
14
+ # * Neither the name of Google Inc. nor the names of its
15
+ # contributors may be used to endorse or promote products derived from
16
+ # this software without specific prior written permission.
17
+ #
18
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22
+ # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
+ # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+
30
+ require 'grpc'
31
+
32
+ # GRPC contains the General RPC module.
33
+ module GRPC
34
+ module Core
35
+ # TimeConsts is a module from the C extension.
36
+ #
37
+ # Here it's re-opened to add a utility func.
38
+ module TimeConsts
39
+ # Converts a time delta to an absolute deadline.
40
+ #
41
+ # Assumes timeish is a relative time, and converts its to an absolute,
42
+ # with following exceptions:
43
+ #
44
+ # * if timish is one of the TimeConsts.TimeSpec constants the value is
45
+ # preserved.
46
+ # * timish < 0 => TimeConsts.INFINITE_FUTURE
47
+ # * timish == 0 => TimeConsts.ZERO
48
+ #
49
+ # @param timeish [Number|TimeSpec]
50
+ # @return timeish [Number|TimeSpec]
51
+ def from_relative_time(timeish)
52
+ if timeish.is_a? TimeSpec
53
+ timeish
54
+ elsif timeish.nil?
55
+ TimeConsts::ZERO
56
+ elsif !timeish.is_a? Numeric
57
+ fail(TypeError,
58
+ "Cannot make an absolute deadline from #{timeish.inspect}")
59
+ elsif timeish < 0
60
+ TimeConsts::INFINITE_FUTURE
61
+ elsif timeish == 0
62
+ TimeConsts::ZERO
63
+ else
64
+ Time.now + timeish
65
+ end
66
+ end
67
+
68
+ module_function :from_relative_time
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,61 @@
1
+ # Copyright 2015, Google Inc.
2
+ # All rights reserved.
3
+ #
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are
6
+ # met:
7
+ #
8
+ # * Redistributions of source code must retain the above copyright
9
+ # notice, this list of conditions and the following disclaimer.
10
+ # * Redistributions in binary form must reproduce the above
11
+ # copyright notice, this list of conditions and the following disclaimer
12
+ # in the documentation and/or other materials provided with the
13
+ # distribution.
14
+ # * Neither the name of Google Inc. nor the names of its
15
+ # contributors may be used to endorse or promote products derived from
16
+ # this software without specific prior written permission.
17
+ #
18
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22
+ # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
+ # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+
30
+ require 'grpc'
31
+
32
+ # GRPC contains the General RPC module.
33
+ module GRPC
34
+ # OutOfTime is an exception class that indicates that an RPC exceeded its
35
+ # deadline.
36
+ OutOfTime = Class.new(StandardError)
37
+
38
+ # BadStatus is an exception class that indicates that an error occurred at
39
+ # either end of a GRPC connection. When raised, it indicates that a status
40
+ # error should be returned to the other end of a GRPC connection; when
41
+ # caught it means that this end received a status error.
42
+ class BadStatus < StandardError
43
+ attr_reader :code, :details
44
+
45
+ # @param code [Numeric] the status code
46
+ # @param details [String] the details of the exception
47
+ def initialize(code, details = 'unknown cause')
48
+ super("#{code}:#{details}")
49
+ @code = code
50
+ @details = details
51
+ end
52
+
53
+ # Converts the exception to a GRPC::Status for use in the networking
54
+ # wrapper layer.
55
+ #
56
+ # @return [Status] with the same code and details
57
+ def to_status
58
+ Status.new(code, details)
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,536 @@
1
+ # Copyright 2015, Google Inc.
2
+ # All rights reserved.
3
+ #
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are
6
+ # met:
7
+ #
8
+ # * Redistributions of source code must retain the above copyright
9
+ # notice, this list of conditions and the following disclaimer.
10
+ # * Redistributions in binary form must reproduce the above
11
+ # copyright notice, this list of conditions and the following disclaimer
12
+ # in the documentation and/or other materials provided with the
13
+ # distribution.
14
+ # * Neither the name of Google Inc. nor the names of its
15
+ # contributors may be used to endorse or promote products derived from
16
+ # this software without specific prior written permission.
17
+ #
18
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22
+ # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
+ # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+
30
+ require 'forwardable'
31
+ require 'grpc/generic/bidi_call'
32
+
33
+ def assert_event_type(ev, want)
34
+ fail OutOfTime if ev.nil?
35
+ got = ev.type
36
+ fail "Unexpected rpc event: got #{got}, want #{want}" unless got == want
37
+ end
38
+
39
+ # GRPC contains the General RPC module.
40
+ module GRPC
41
+ # The ActiveCall class provides simple methods for sending marshallable
42
+ # data to a call
43
+ class ActiveCall
44
+ include Core::CompletionType
45
+ include Core::StatusCodes
46
+ include Core::TimeConsts
47
+ attr_reader(:deadline)
48
+
49
+ # client_invoke begins a client invocation.
50
+ #
51
+ # Flow Control note: this blocks until flow control accepts that client
52
+ # request can go ahead.
53
+ #
54
+ # deadline is the absolute deadline for the call.
55
+ #
56
+ # == Keyword Arguments ==
57
+ # any keyword arguments are treated as metadata to be sent to the server
58
+ # if a keyword value is a list, multiple metadata for it's key are sent
59
+ #
60
+ # @param call [Call] a call on which to start and invocation
61
+ # @param q [CompletionQueue] the completion queue
62
+ # @param deadline [Fixnum,TimeSpec] the deadline
63
+ def self.client_invoke(call, q, _deadline, **kw)
64
+ fail(ArgumentError, 'not a call') unless call.is_a? Core::Call
65
+ unless q.is_a? Core::CompletionQueue
66
+ fail(ArgumentError, 'not a CompletionQueue')
67
+ end
68
+ call.add_metadata(kw) if kw.length > 0
69
+ client_metadata_read = Object.new
70
+ finished_tag = Object.new
71
+ call.invoke(q, client_metadata_read, finished_tag)
72
+ [finished_tag, client_metadata_read]
73
+ end
74
+
75
+ # Creates an ActiveCall.
76
+ #
77
+ # ActiveCall should only be created after a call is accepted. That
78
+ # means different things on a client and a server. On the client, the
79
+ # call is accepted after calling call.invoke. On the server, this is
80
+ # after call.accept.
81
+ #
82
+ # #initialize cannot determine if the call is accepted or not; so if a
83
+ # call that's not accepted is used here, the error won't be visible until
84
+ # the ActiveCall methods are called.
85
+ #
86
+ # deadline is the absolute deadline for the call.
87
+ #
88
+ # @param call [Call] the call used by the ActiveCall
89
+ # @param q [CompletionQueue] the completion queue used to accept
90
+ # the call
91
+ # @param marshal [Function] f(obj)->string that marshal requests
92
+ # @param unmarshal [Function] f(string)->obj that unmarshals responses
93
+ # @param deadline [Fixnum] the deadline for the call to complete
94
+ # @param finished_tag [Object] the object used as the call's finish tag,
95
+ # if the call has begun
96
+ # @param read_metadata_tag [Object] the object used as the call's finish
97
+ # tag, if the call has begun
98
+ # @param started [true|false] indicates if the call has begun
99
+ def initialize(call, q, marshal, unmarshal, deadline, finished_tag: nil,
100
+ read_metadata_tag: nil, started: true)
101
+ fail(ArgumentError, 'not a call') unless call.is_a? Core::Call
102
+ unless q.is_a? Core::CompletionQueue
103
+ fail(ArgumentError, 'not a CompletionQueue')
104
+ end
105
+ @call = call
106
+ @cq = q
107
+ @deadline = deadline
108
+ @finished_tag = finished_tag
109
+ @read_metadata_tag = read_metadata_tag
110
+ @marshal = marshal
111
+ @started = started
112
+ @unmarshal = unmarshal
113
+ end
114
+
115
+ # Obtains the status of the call.
116
+ #
117
+ # this value is nil until the call completes
118
+ # @return this call's status
119
+ def status
120
+ @call.status
121
+ end
122
+
123
+ # Obtains the metadata of the call.
124
+ #
125
+ # At the start of the call this will be nil. During the call this gets
126
+ # some values as soon as the other end of the connection acknowledges the
127
+ # request.
128
+ #
129
+ # @return this calls's metadata
130
+ def metadata
131
+ @call.metadata
132
+ end
133
+
134
+ # Cancels the call.
135
+ #
136
+ # Cancels the call. The call does not return any result, but once this it
137
+ # has been called, the call should eventually terminate. Due to potential
138
+ # races between the execution of the cancel and the in-flight request, the
139
+ # result of the call after calling #cancel is indeterminate:
140
+ #
141
+ # - the call may terminate with a BadStatus exception, with code=CANCELLED
142
+ # - the call may terminate with OK Status, and return a response
143
+ # - the call may terminate with a different BadStatus exception if that
144
+ # was happening
145
+ def cancel
146
+ @call.cancel
147
+ end
148
+
149
+ # indicates if the call is shutdown
150
+ def shutdown
151
+ @shutdown ||= false
152
+ end
153
+
154
+ # indicates if the call is cancelled.
155
+ def cancelled
156
+ @cancelled ||= false
157
+ end
158
+
159
+ # multi_req_view provides a restricted view of this ActiveCall for use
160
+ # in a server client-streaming handler.
161
+ def multi_req_view
162
+ MultiReqView.new(self)
163
+ end
164
+
165
+ # single_req_view provides a restricted view of this ActiveCall for use in
166
+ # a server request-response handler.
167
+ def single_req_view
168
+ SingleReqView.new(self)
169
+ end
170
+
171
+ # operation provides a restricted view of this ActiveCall for use as
172
+ # a Operation.
173
+ def operation
174
+ Operation.new(self)
175
+ end
176
+
177
+ # writes_done indicates that all writes are completed.
178
+ #
179
+ # It blocks until the remote endpoint acknowledges by sending a FINISHED
180
+ # event, unless assert_finished is set to false. Any calls to
181
+ # #remote_send after this call will fail.
182
+ #
183
+ # @param assert_finished [true, false] when true(default), waits for
184
+ # FINISHED.
185
+ def writes_done(assert_finished = true)
186
+ @call.writes_done(self)
187
+ ev = @cq.pluck(self, INFINITE_FUTURE)
188
+ begin
189
+ assert_event_type(ev, FINISH_ACCEPTED)
190
+ logger.debug("Writes done: waiting for finish? #{assert_finished}")
191
+ ensure
192
+ ev.close
193
+ end
194
+
195
+ return unless assert_finished
196
+ ev = @cq.pluck(@finished_tag, INFINITE_FUTURE)
197
+ fail 'unexpected nil event' if ev.nil?
198
+ ev.close
199
+ @call.status
200
+ end
201
+
202
+ # finished waits until the call is completed.
203
+ #
204
+ # It blocks until the remote endpoint acknowledges by sending a FINISHED
205
+ # event.
206
+ def finished
207
+ ev = @cq.pluck(@finished_tag, INFINITE_FUTURE)
208
+ begin
209
+ fail "unexpected event: #{ev.inspect}" unless ev.type == FINISHED
210
+ if @call.metadata.nil?
211
+ @call.metadata = ev.result.metadata
212
+ else
213
+ @call.metadata.merge!(ev.result.metadata)
214
+ end
215
+
216
+ if ev.result.code != Core::StatusCodes::OK
217
+ fail BadStatus.new(ev.result.code, ev.result.details)
218
+ end
219
+ res = ev.result
220
+ ensure
221
+ ev.close
222
+ end
223
+ res
224
+ end
225
+
226
+ # remote_send sends a request to the remote endpoint.
227
+ #
228
+ # It blocks until the remote endpoint acknowledges by sending a
229
+ # WRITE_ACCEPTED. req can be marshalled already.
230
+ #
231
+ # @param req [Object, String] the object to send or it's marshal form.
232
+ # @param marshalled [false, true] indicates if the object is already
233
+ # marshalled.
234
+ def remote_send(req, marshalled = false)
235
+ assert_queue_is_ready
236
+ logger.debug("sending #{req.inspect}, marshalled? #{marshalled}")
237
+ if marshalled
238
+ payload = req
239
+ else
240
+ payload = @marshal.call(req)
241
+ end
242
+ @call.start_write(Core::ByteBuffer.new(payload), self)
243
+
244
+ # call queue#pluck, and wait for WRITE_ACCEPTED, so as not to return
245
+ # until the flow control allows another send on this call.
246
+ ev = @cq.pluck(self, INFINITE_FUTURE)
247
+ begin
248
+ assert_event_type(ev, WRITE_ACCEPTED)
249
+ ensure
250
+ ev.close
251
+ end
252
+ end
253
+
254
+ # send_status sends a status to the remote endpoint
255
+ #
256
+ # @param code [int] the status code to send
257
+ # @param details [String] details
258
+ # @param assert_finished [true, false] when true(default), waits for
259
+ # FINISHED.
260
+ def send_status(code = OK, details = '', assert_finished = false)
261
+ assert_queue_is_ready
262
+ @call.start_write_status(code, details, self)
263
+ ev = @cq.pluck(self, INFINITE_FUTURE)
264
+ begin
265
+ assert_event_type(ev, FINISH_ACCEPTED)
266
+ ensure
267
+ ev.close
268
+ end
269
+ logger.debug("Status sent: #{code}:'#{details}'")
270
+ return finished if assert_finished
271
+ nil
272
+ end
273
+
274
+ # remote_read reads a response from the remote endpoint.
275
+ #
276
+ # It blocks until the remote endpoint sends a READ or FINISHED event. On
277
+ # a READ, it returns the response after unmarshalling it. On
278
+ # FINISHED, it returns nil if the status is OK, otherwise raising
279
+ # BadStatus
280
+ def remote_read
281
+ if @call.metadata.nil? && !@read_metadata_tag.nil?
282
+ ev = @cq.pluck(@read_metadata_tag, INFINITE_FUTURE)
283
+ assert_event_type(ev, CLIENT_METADATA_READ)
284
+ @call.metadata = ev.result
285
+ @read_metadata_tag = nil
286
+ end
287
+
288
+ @call.start_read(self)
289
+ ev = @cq.pluck(self, INFINITE_FUTURE)
290
+ begin
291
+ assert_event_type(ev, READ)
292
+ logger.debug("received req: #{ev.result.inspect}")
293
+ unless ev.result.nil?
294
+ logger.debug("received req.to_s: #{ev.result}")
295
+ res = @unmarshal.call(ev.result.to_s)
296
+ logger.debug("received_req (unmarshalled): #{res.inspect}")
297
+ return res
298
+ end
299
+ ensure
300
+ ev.close
301
+ end
302
+ logger.debug('found nil; the final response has been sent')
303
+ nil
304
+ end
305
+
306
+ # each_remote_read passes each response to the given block or returns an
307
+ # enumerator the responses if no block is given.
308
+ #
309
+ # == Enumerator ==
310
+ #
311
+ # * #next blocks until the remote endpoint sends a READ or FINISHED
312
+ # * for each read, enumerator#next yields the response
313
+ # * on status
314
+ # * if it's is OK, enumerator#next raises StopException
315
+ # * if is not OK, enumerator#next raises RuntimeException
316
+ #
317
+ # == Block ==
318
+ #
319
+ # * if provided it is executed for each response
320
+ # * the call blocks until no more responses are provided
321
+ #
322
+ # @return [Enumerator] if no block was given
323
+ def each_remote_read
324
+ return enum_for(:each_remote_read) unless block_given?
325
+ loop do
326
+ resp = remote_read
327
+ break if resp.is_a? Struct::Status # is an OK status
328
+ break if resp.nil? # the last response was received
329
+ yield resp
330
+ end
331
+ end
332
+
333
+ # each_remote_read_then_finish passes each response to the given block or
334
+ # returns an enumerator of the responses if no block is given.
335
+ #
336
+ # It is like each_remote_read, but it blocks on finishing on detecting
337
+ # the final message.
338
+ #
339
+ # == Enumerator ==
340
+ #
341
+ # * #next blocks until the remote endpoint sends a READ or FINISHED
342
+ # * for each read, enumerator#next yields the response
343
+ # * on status
344
+ # * if it's is OK, enumerator#next raises StopException
345
+ # * if is not OK, enumerator#next raises RuntimeException
346
+ #
347
+ # == Block ==
348
+ #
349
+ # * if provided it is executed for each response
350
+ # * the call blocks until no more responses are provided
351
+ #
352
+ # @return [Enumerator] if no block was given
353
+ def each_remote_read_then_finish
354
+ return enum_for(:each_remote_read_then_finish) unless block_given?
355
+ loop do
356
+ resp = remote_read
357
+ break if resp.is_a? Struct::Status # is an OK status
358
+ if resp.nil? # the last response was received, but not finished yet
359
+ finished
360
+ break
361
+ end
362
+ yield resp
363
+ end
364
+ end
365
+
366
+ # request_response sends a request to a GRPC server, and returns the
367
+ # response.
368
+ #
369
+ # == Keyword Arguments ==
370
+ # any keyword arguments are treated as metadata to be sent to the server
371
+ # if a keyword value is a list, multiple metadata for it's key are sent
372
+ #
373
+ # @param req [Object] the request sent to the server
374
+ # @return [Object] the response received from the server
375
+ def request_response(req, **kw)
376
+ start_call(**kw) unless @started
377
+ remote_send(req)
378
+ writes_done(false)
379
+ response = remote_read
380
+ finished unless response.is_a? Struct::Status
381
+ response
382
+ end
383
+
384
+ # client_streamer sends a stream of requests to a GRPC server, and
385
+ # returns a single response.
386
+ #
387
+ # requests provides an 'iterable' of Requests. I.e. it follows Ruby's
388
+ # #each enumeration protocol. In the simplest case, requests will be an
389
+ # array of marshallable objects; in typical case it will be an Enumerable
390
+ # that allows dynamic construction of the marshallable objects.
391
+ #
392
+ # == Keyword Arguments ==
393
+ # any keyword arguments are treated as metadata to be sent to the server
394
+ # if a keyword value is a list, multiple metadata for it's key are sent
395
+ #
396
+ # @param requests [Object] an Enumerable of requests to send
397
+ # @return [Object] the response received from the server
398
+ def client_streamer(requests, **kw)
399
+ start_call(**kw) unless @started
400
+ requests.each { |r| remote_send(r) }
401
+ writes_done(false)
402
+ response = remote_read
403
+ finished unless response.is_a? Struct::Status
404
+ response
405
+ end
406
+
407
+ # server_streamer sends one request to the GRPC server, which yields a
408
+ # stream of responses.
409
+ #
410
+ # responses provides an enumerator over the streamed responses, i.e. it
411
+ # follows Ruby's #each iteration protocol. The enumerator blocks while
412
+ # waiting for each response, stops when the server signals that no
413
+ # further responses will be supplied. If the implicit block is provided,
414
+ # it is executed with each response as the argument and no result is
415
+ # returned.
416
+ #
417
+ # == Keyword Arguments ==
418
+ # any keyword arguments are treated as metadata to be sent to the server
419
+ # if a keyword value is a list, multiple metadata for it's key are sent
420
+ # any keyword arguments are treated as metadata to be sent to the server.
421
+ #
422
+ # @param req [Object] the request sent to the server
423
+ # @return [Enumerator|nil] a response Enumerator
424
+ def server_streamer(req, **kw)
425
+ start_call(**kw) unless @started
426
+ remote_send(req)
427
+ writes_done(false)
428
+ replies = enum_for(:each_remote_read_then_finish)
429
+ return replies unless block_given?
430
+ replies.each { |r| yield r }
431
+ end
432
+
433
+ # bidi_streamer sends a stream of requests to the GRPC server, and yields
434
+ # a stream of responses.
435
+ #
436
+ # This method takes an Enumerable of requests, and returns and enumerable
437
+ # of responses.
438
+ #
439
+ # == requests ==
440
+ #
441
+ # requests provides an 'iterable' of Requests. I.e. it follows Ruby's
442
+ # #each enumeration protocol. In the simplest case, requests will be an
443
+ # array of marshallable objects; in typical case it will be an
444
+ # Enumerable that allows dynamic construction of the marshallable
445
+ # objects.
446
+ #
447
+ # == responses ==
448
+ #
449
+ # This is an enumerator of responses. I.e, its #next method blocks
450
+ # waiting for the next response. Also, if at any point the block needs
451
+ # to consume all the remaining responses, this can be done using #each or
452
+ # #collect. Calling #each or #collect should only be done if
453
+ # the_call#writes_done has been called, otherwise the block will loop
454
+ # forever.
455
+ #
456
+ # == Keyword Arguments ==
457
+ # any keyword arguments are treated as metadata to be sent to the server
458
+ # if a keyword value is a list, multiple metadata for it's key are sent
459
+ #
460
+ # @param requests [Object] an Enumerable of requests to send
461
+ # @return [Enumerator, nil] a response Enumerator
462
+ def bidi_streamer(requests, **kw, &blk)
463
+ start_call(**kw) unless @started
464
+ bd = BidiCall.new(@call, @cq, @marshal, @unmarshal, @deadline,
465
+ @finished_tag)
466
+ bd.run_on_client(requests, &blk)
467
+ end
468
+
469
+ # run_server_bidi orchestrates a BiDi stream processing on a server.
470
+ #
471
+ # N.B. gen_each_reply is a func(Enumerable<Requests>)
472
+ #
473
+ # It takes an enumerable of requests as an arg, in case there is a
474
+ # relationship between the stream of requests and the stream of replies.
475
+ #
476
+ # This does not mean that must necessarily be one. E.g, the replies
477
+ # produced by gen_each_reply could ignore the received_msgs
478
+ #
479
+ # @param gen_each_reply [Proc] generates the BiDi stream replies
480
+ def run_server_bidi(gen_each_reply)
481
+ bd = BidiCall.new(@call, @cq, @marshal, @unmarshal, @deadline,
482
+ @finished_tag)
483
+ bd.run_on_server(gen_each_reply)
484
+ end
485
+
486
+ private
487
+
488
+ def start_call(**kw)
489
+ tags = ActiveCall.client_invoke(@call, @cq, @deadline, **kw)
490
+ @finished_tag, @read_metadata_tag = tags
491
+ @started = true
492
+ end
493
+
494
+ def self.view_class(*visible_methods)
495
+ Class.new do
496
+ extend ::Forwardable
497
+ def_delegators :@wrapped, *visible_methods
498
+
499
+ # @param wrapped [ActiveCall] the call whose methods are shielded
500
+ def initialize(wrapped)
501
+ @wrapped = wrapped
502
+ end
503
+ end
504
+ end
505
+
506
+ # SingleReqView limits access to an ActiveCall's methods for use in server
507
+ # handlers that receive just one request.
508
+ SingleReqView = view_class(:cancelled, :deadline)
509
+
510
+ # MultiReqView limits access to an ActiveCall's methods for use in
511
+ # server client_streamer handlers.
512
+ MultiReqView = view_class(:cancelled, :deadline, :each_queued_msg,
513
+ :each_remote_read)
514
+
515
+ # Operation limits access to an ActiveCall's methods for use as
516
+ # a Operation on the client.
517
+ Operation = view_class(:cancel, :cancelled, :deadline, :execute,
518
+ :metadata, :status)
519
+
520
+ # confirms that no events are enqueued, and that the queue is not
521
+ # shutdown.
522
+ def assert_queue_is_ready
523
+ ev = nil
524
+ begin
525
+ ev = @cq.pluck(self, ZERO)
526
+ fail "unexpected event #{ev.inspect}" unless ev.nil?
527
+ rescue OutOfTime
528
+ logging.debug('timed out waiting for next event')
529
+ # expected, nothing should be on the queue and the deadline was ZERO,
530
+ # except things using another tag
531
+ ensure
532
+ ev.close unless ev.nil?
533
+ end
534
+ end
535
+ end
536
+ end