protobuf-cucumber 3.10.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (204) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +21 -0
  3. data/.rubocop.yml +70 -0
  4. data/.rubocop_todo.yml +145 -0
  5. data/.travis.yml +40 -0
  6. data/.yardopts +5 -0
  7. data/CHANGES.md +344 -0
  8. data/CONTRIBUTING.md +16 -0
  9. data/Gemfile +3 -0
  10. data/LICENSE.txt +22 -0
  11. data/README.md +33 -0
  12. data/Rakefile +64 -0
  13. data/bin/protoc-gen-ruby +22 -0
  14. data/bin/rpc_server +5 -0
  15. data/install-protobuf.sh +28 -0
  16. data/lib/protobuf.rb +129 -0
  17. data/lib/protobuf/cli.rb +257 -0
  18. data/lib/protobuf/code_generator.rb +120 -0
  19. data/lib/protobuf/decoder.rb +28 -0
  20. data/lib/protobuf/deprecation.rb +117 -0
  21. data/lib/protobuf/descriptors.rb +3 -0
  22. data/lib/protobuf/descriptors/google/protobuf/compiler/plugin.pb.rb +62 -0
  23. data/lib/protobuf/descriptors/google/protobuf/descriptor.pb.rb +301 -0
  24. data/lib/protobuf/encoder.rb +11 -0
  25. data/lib/protobuf/enum.rb +365 -0
  26. data/lib/protobuf/exceptions.rb +9 -0
  27. data/lib/protobuf/field.rb +74 -0
  28. data/lib/protobuf/field/base_field.rb +380 -0
  29. data/lib/protobuf/field/base_field_object_definitions.rb +504 -0
  30. data/lib/protobuf/field/bool_field.rb +64 -0
  31. data/lib/protobuf/field/bytes_field.rb +78 -0
  32. data/lib/protobuf/field/double_field.rb +25 -0
  33. data/lib/protobuf/field/enum_field.rb +61 -0
  34. data/lib/protobuf/field/field_array.rb +104 -0
  35. data/lib/protobuf/field/field_hash.rb +122 -0
  36. data/lib/protobuf/field/fixed32_field.rb +25 -0
  37. data/lib/protobuf/field/fixed64_field.rb +28 -0
  38. data/lib/protobuf/field/float_field.rb +43 -0
  39. data/lib/protobuf/field/int32_field.rb +21 -0
  40. data/lib/protobuf/field/int64_field.rb +34 -0
  41. data/lib/protobuf/field/integer_field.rb +23 -0
  42. data/lib/protobuf/field/message_field.rb +51 -0
  43. data/lib/protobuf/field/sfixed32_field.rb +27 -0
  44. data/lib/protobuf/field/sfixed64_field.rb +28 -0
  45. data/lib/protobuf/field/signed_integer_field.rb +29 -0
  46. data/lib/protobuf/field/sint32_field.rb +21 -0
  47. data/lib/protobuf/field/sint64_field.rb +21 -0
  48. data/lib/protobuf/field/string_field.rb +51 -0
  49. data/lib/protobuf/field/uint32_field.rb +21 -0
  50. data/lib/protobuf/field/uint64_field.rb +21 -0
  51. data/lib/protobuf/field/varint_field.rb +77 -0
  52. data/lib/protobuf/generators/base.rb +85 -0
  53. data/lib/protobuf/generators/enum_generator.rb +39 -0
  54. data/lib/protobuf/generators/extension_generator.rb +27 -0
  55. data/lib/protobuf/generators/field_generator.rb +193 -0
  56. data/lib/protobuf/generators/file_generator.rb +262 -0
  57. data/lib/protobuf/generators/group_generator.rb +122 -0
  58. data/lib/protobuf/generators/message_generator.rb +104 -0
  59. data/lib/protobuf/generators/option_generator.rb +17 -0
  60. data/lib/protobuf/generators/printable.rb +160 -0
  61. data/lib/protobuf/generators/service_generator.rb +50 -0
  62. data/lib/protobuf/lifecycle.rb +33 -0
  63. data/lib/protobuf/logging.rb +39 -0
  64. data/lib/protobuf/message.rb +260 -0
  65. data/lib/protobuf/message/fields.rb +233 -0
  66. data/lib/protobuf/message/serialization.rb +85 -0
  67. data/lib/protobuf/optionable.rb +70 -0
  68. data/lib/protobuf/rpc/buffer.rb +78 -0
  69. data/lib/protobuf/rpc/client.rb +140 -0
  70. data/lib/protobuf/rpc/connectors/base.rb +221 -0
  71. data/lib/protobuf/rpc/connectors/ping.rb +89 -0
  72. data/lib/protobuf/rpc/connectors/socket.rb +78 -0
  73. data/lib/protobuf/rpc/connectors/zmq.rb +319 -0
  74. data/lib/protobuf/rpc/dynamic_discovery.pb.rb +50 -0
  75. data/lib/protobuf/rpc/env.rb +60 -0
  76. data/lib/protobuf/rpc/error.rb +28 -0
  77. data/lib/protobuf/rpc/error/client_error.rb +31 -0
  78. data/lib/protobuf/rpc/error/server_error.rb +43 -0
  79. data/lib/protobuf/rpc/middleware.rb +25 -0
  80. data/lib/protobuf/rpc/middleware/exception_handler.rb +40 -0
  81. data/lib/protobuf/rpc/middleware/logger.rb +95 -0
  82. data/lib/protobuf/rpc/middleware/request_decoder.rb +79 -0
  83. data/lib/protobuf/rpc/middleware/response_encoder.rb +83 -0
  84. data/lib/protobuf/rpc/middleware/runner.rb +18 -0
  85. data/lib/protobuf/rpc/rpc.pb.rb +64 -0
  86. data/lib/protobuf/rpc/rpc_method.rb +16 -0
  87. data/lib/protobuf/rpc/server.rb +39 -0
  88. data/lib/protobuf/rpc/servers/socket/server.rb +121 -0
  89. data/lib/protobuf/rpc/servers/socket/worker.rb +56 -0
  90. data/lib/protobuf/rpc/servers/socket_runner.rb +46 -0
  91. data/lib/protobuf/rpc/servers/zmq/broker.rb +194 -0
  92. data/lib/protobuf/rpc/servers/zmq/server.rb +321 -0
  93. data/lib/protobuf/rpc/servers/zmq/util.rb +48 -0
  94. data/lib/protobuf/rpc/servers/zmq/worker.rb +105 -0
  95. data/lib/protobuf/rpc/servers/zmq_runner.rb +70 -0
  96. data/lib/protobuf/rpc/service.rb +172 -0
  97. data/lib/protobuf/rpc/service_directory.rb +261 -0
  98. data/lib/protobuf/rpc/service_dispatcher.rb +45 -0
  99. data/lib/protobuf/rpc/service_filters.rb +250 -0
  100. data/lib/protobuf/rpc/stat.rb +119 -0
  101. data/lib/protobuf/socket.rb +21 -0
  102. data/lib/protobuf/tasks.rb +1 -0
  103. data/lib/protobuf/tasks/compile.rake +80 -0
  104. data/lib/protobuf/varint.rb +20 -0
  105. data/lib/protobuf/varint_pure.rb +31 -0
  106. data/lib/protobuf/version.rb +3 -0
  107. data/lib/protobuf/wire_type.rb +10 -0
  108. data/lib/protobuf/zmq.rb +21 -0
  109. data/profile.html +5103 -0
  110. data/proto/dynamic_discovery.proto +44 -0
  111. data/proto/google/protobuf/compiler/plugin.proto +147 -0
  112. data/proto/google/protobuf/descriptor.proto +779 -0
  113. data/proto/rpc.proto +69 -0
  114. data/protobuf-cucumber.gemspec +57 -0
  115. data/spec/benchmark/tasks.rb +143 -0
  116. data/spec/bin/protoc-gen-ruby_spec.rb +23 -0
  117. data/spec/encoding/all_types_spec.rb +103 -0
  118. data/spec/encoding/extreme_values_spec.rb +0 -0
  119. data/spec/functional/class_inheritance_spec.rb +52 -0
  120. data/spec/functional/code_generator_spec.rb +58 -0
  121. data/spec/functional/socket_server_spec.rb +59 -0
  122. data/spec/functional/zmq_server_spec.rb +105 -0
  123. data/spec/lib/protobuf/cli_spec.rb +317 -0
  124. data/spec/lib/protobuf/code_generator_spec.rb +87 -0
  125. data/spec/lib/protobuf/enum_spec.rb +307 -0
  126. data/spec/lib/protobuf/field/bool_field_spec.rb +91 -0
  127. data/spec/lib/protobuf/field/double_field_spec.rb +9 -0
  128. data/spec/lib/protobuf/field/enum_field_spec.rb +44 -0
  129. data/spec/lib/protobuf/field/field_array_spec.rb +105 -0
  130. data/spec/lib/protobuf/field/field_hash_spec.rb +168 -0
  131. data/spec/lib/protobuf/field/fixed32_field_spec.rb +7 -0
  132. data/spec/lib/protobuf/field/fixed64_field_spec.rb +7 -0
  133. data/spec/lib/protobuf/field/float_field_spec.rb +90 -0
  134. data/spec/lib/protobuf/field/int32_field_spec.rb +120 -0
  135. data/spec/lib/protobuf/field/int64_field_spec.rb +7 -0
  136. data/spec/lib/protobuf/field/message_field_spec.rb +132 -0
  137. data/spec/lib/protobuf/field/sfixed32_field_spec.rb +9 -0
  138. data/spec/lib/protobuf/field/sfixed64_field_spec.rb +9 -0
  139. data/spec/lib/protobuf/field/sint32_field_spec.rb +9 -0
  140. data/spec/lib/protobuf/field/sint64_field_spec.rb +9 -0
  141. data/spec/lib/protobuf/field/string_field_spec.rb +79 -0
  142. data/spec/lib/protobuf/field/uint32_field_spec.rb +7 -0
  143. data/spec/lib/protobuf/field/uint64_field_spec.rb +7 -0
  144. data/spec/lib/protobuf/field_spec.rb +192 -0
  145. data/spec/lib/protobuf/generators/base_spec.rb +154 -0
  146. data/spec/lib/protobuf/generators/enum_generator_spec.rb +82 -0
  147. data/spec/lib/protobuf/generators/extension_generator_spec.rb +42 -0
  148. data/spec/lib/protobuf/generators/field_generator_spec.rb +197 -0
  149. data/spec/lib/protobuf/generators/file_generator_spec.rb +119 -0
  150. data/spec/lib/protobuf/generators/message_generator_spec.rb +0 -0
  151. data/spec/lib/protobuf/generators/service_generator_spec.rb +99 -0
  152. data/spec/lib/protobuf/lifecycle_spec.rb +94 -0
  153. data/spec/lib/protobuf/message_spec.rb +944 -0
  154. data/spec/lib/protobuf/optionable_spec.rb +265 -0
  155. data/spec/lib/protobuf/rpc/client_spec.rb +66 -0
  156. data/spec/lib/protobuf/rpc/connectors/base_spec.rb +226 -0
  157. data/spec/lib/protobuf/rpc/connectors/ping_spec.rb +69 -0
  158. data/spec/lib/protobuf/rpc/connectors/socket_spec.rb +34 -0
  159. data/spec/lib/protobuf/rpc/connectors/zmq_spec.rb +110 -0
  160. data/spec/lib/protobuf/rpc/middleware/exception_handler_spec.rb +62 -0
  161. data/spec/lib/protobuf/rpc/middleware/logger_spec.rb +49 -0
  162. data/spec/lib/protobuf/rpc/middleware/request_decoder_spec.rb +115 -0
  163. data/spec/lib/protobuf/rpc/middleware/response_encoder_spec.rb +91 -0
  164. data/spec/lib/protobuf/rpc/servers/socket_server_spec.rb +38 -0
  165. data/spec/lib/protobuf/rpc/servers/zmq/server_spec.rb +43 -0
  166. data/spec/lib/protobuf/rpc/servers/zmq/util_spec.rb +55 -0
  167. data/spec/lib/protobuf/rpc/servers/zmq/worker_spec.rb +35 -0
  168. data/spec/lib/protobuf/rpc/service_directory_spec.rb +293 -0
  169. data/spec/lib/protobuf/rpc/service_dispatcher_spec.rb +35 -0
  170. data/spec/lib/protobuf/rpc/service_filters_spec.rb +517 -0
  171. data/spec/lib/protobuf/rpc/service_spec.rb +162 -0
  172. data/spec/lib/protobuf/rpc/stat_spec.rb +101 -0
  173. data/spec/lib/protobuf/varint_spec.rb +29 -0
  174. data/spec/lib/protobuf_spec.rb +105 -0
  175. data/spec/spec_helper.rb +42 -0
  176. data/spec/support/all.rb +6 -0
  177. data/spec/support/packed_field.rb +23 -0
  178. data/spec/support/protos/all_types.data.bin +0 -0
  179. data/spec/support/protos/all_types.data.txt +119 -0
  180. data/spec/support/protos/enum.pb.rb +63 -0
  181. data/spec/support/protos/enum.proto +37 -0
  182. data/spec/support/protos/extreme_values.data.bin +0 -0
  183. data/spec/support/protos/google_unittest.bin +0 -0
  184. data/spec/support/protos/google_unittest.pb.rb +798 -0
  185. data/spec/support/protos/google_unittest.proto +884 -0
  186. data/spec/support/protos/google_unittest_custom_options.bin +0 -0
  187. data/spec/support/protos/google_unittest_custom_options.pb.rb +361 -0
  188. data/spec/support/protos/google_unittest_custom_options.proto +424 -0
  189. data/spec/support/protos/google_unittest_import.pb.rb +55 -0
  190. data/spec/support/protos/google_unittest_import.proto +73 -0
  191. data/spec/support/protos/google_unittest_import_public.pb.rb +31 -0
  192. data/spec/support/protos/google_unittest_import_public.proto +41 -0
  193. data/spec/support/protos/map-test.bin +157 -0
  194. data/spec/support/protos/map-test.pb.rb +85 -0
  195. data/spec/support/protos/map-test.proto +68 -0
  196. data/spec/support/protos/multi_field_extensions.pb.rb +59 -0
  197. data/spec/support/protos/multi_field_extensions.proto +35 -0
  198. data/spec/support/protos/resource.pb.rb +172 -0
  199. data/spec/support/protos/resource.proto +137 -0
  200. data/spec/support/resource_service.rb +23 -0
  201. data/spec/support/server.rb +65 -0
  202. data/spec/support/test_app_file.rb +2 -0
  203. data/varint_prof.rb +82 -0
  204. metadata +579 -0
@@ -0,0 +1,85 @@
1
+ require 'stringio'
2
+ require 'protobuf/decoder'
3
+ require 'protobuf/encoder'
4
+
5
+ module Protobuf
6
+ class Message
7
+ module Serialization
8
+
9
+ module ClassMethods
10
+ def decode(bytes)
11
+ new.decode(bytes)
12
+ end
13
+
14
+ def decode_from(stream)
15
+ new.decode_from(stream)
16
+ end
17
+
18
+ # Create a new object with the given values and return the encoded bytes.
19
+ def encode(fields = {})
20
+ new(fields).encode
21
+ end
22
+ end
23
+
24
+ def self.included(other)
25
+ other.extend(ClassMethods)
26
+ end
27
+
28
+ ##
29
+ # Instance Methods
30
+ #
31
+
32
+ # Decode the given non-stream bytes into this message.
33
+ #
34
+ def decode(bytes)
35
+ decode_from(::StringIO.new(bytes))
36
+ end
37
+
38
+ # Decode the given stream into this message.
39
+ #
40
+ def decode_from(stream)
41
+ ::Protobuf::Decoder.decode_each_field(stream) do |tag, bytes|
42
+ set_field_bytes(tag, bytes)
43
+ end
44
+
45
+ self
46
+ end
47
+
48
+ # Encode this message
49
+ #
50
+ def encode
51
+ stream = ::StringIO.new
52
+ stream.set_encoding(::Protobuf::Field::BytesField::BYTES_ENCODING)
53
+ encode_to(stream)
54
+ stream.string
55
+ end
56
+
57
+ # Encode this message to the given stream.
58
+ #
59
+ def encode_to(stream)
60
+ ::Protobuf::Encoder.encode(self, stream)
61
+ end
62
+
63
+ ##
64
+ # Instance Aliases
65
+ #
66
+ alias :parse_from_string decode
67
+ alias :deserialize decode
68
+ alias :parse_from decode_from
69
+ alias :deserialize_from decode_from
70
+ alias :to_s encode
71
+ alias :bytes encode
72
+ alias :serialize encode
73
+ alias :serialize_to_string encode
74
+ alias :serialize_to encode_to
75
+
76
+ private
77
+
78
+ def set_field_bytes(tag, bytes)
79
+ field = _protobuf_message_field[tag]
80
+ field.set(self, bytes) if field
81
+ end
82
+
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,70 @@
1
+ module Protobuf
2
+ module Optionable
3
+ module ClassMethods
4
+ def get_option(name)
5
+ name = name.to_s
6
+ option = optionable_descriptor_class.get_field(name, true)
7
+ fail ArgumentError, "invalid option=#{name}" unless option
8
+ unless option.fully_qualified_name.to_s == name
9
+ # Eventually we'll deprecate the use of simple names of fields completely, but for now make sure people
10
+ # are accessing options correctly. We allow simple names in other places for backwards compatibility.
11
+ fail ArgumentError, "must access option using its fully qualified name: #{option.fully_qualified_name.inspect}"
12
+ end
13
+ value =
14
+ if @_optionable_options.try(:key?, name)
15
+ @_optionable_options[name]
16
+ else
17
+ option.default_value
18
+ end
19
+ if option.type_class < ::Protobuf::Message
20
+ option.type_class.new(value)
21
+ else
22
+ value
23
+ end
24
+ end
25
+
26
+ def get_option!(name)
27
+ get_option(name) if @_optionable_options.try(:key?, name.to_s)
28
+ end
29
+
30
+ private
31
+
32
+ def set_option(name, value = true)
33
+ @_optionable_options ||= {}
34
+ @_optionable_options[name.to_s] = value
35
+ end
36
+ end
37
+
38
+ def get_option(name)
39
+ self.class.get_option(name)
40
+ end
41
+
42
+ def get_option!(name)
43
+ self.class.get_option!(name)
44
+ end
45
+
46
+ def self.inject(base_class, extend_class = true, &block)
47
+ unless block_given?
48
+ fail ArgumentError, 'missing option class block (e.g: ::Google::Protobuf::MessageOptions)'
49
+ end
50
+ if extend_class
51
+ # Check if optionable_descriptor_class is already defined and short circuit if so.
52
+ # File options are injected per module, and since a module can be defined more than once,
53
+ # we will get a warning if we try to define optionable_descriptor_class twice.
54
+ if base_class.respond_to?(:optionable_descriptor_class)
55
+ # Don't define optionable_descriptor_class twice
56
+ return if base_class.optionable_descriptor_class == block.call
57
+
58
+ fail 'A class is being defined with two different descriptor classes, something is very wrong'
59
+ end
60
+
61
+ base_class.extend(ClassMethods)
62
+ base_class.__send__(:include, self)
63
+ base_class.define_singleton_method(:optionable_descriptor_class, block)
64
+ else
65
+ base_class.__send__(:include, ClassMethods)
66
+ base_class.module_eval { define_method(:optionable_descriptor_class, block) }
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,78 @@
1
+ module Protobuf
2
+ module Rpc
3
+ class Buffer
4
+
5
+ attr_accessor :mode, :data, :size
6
+
7
+ MODES = [:read, :write].freeze
8
+
9
+ # constantize this so we don't re-initialize the regex every time we need it
10
+ SIZE_REGEX = /^\d+-/
11
+
12
+ def initialize(mode = :read)
13
+ @flush = false
14
+ @data = ""
15
+ @size = 0
16
+ self.mode = mode
17
+ end
18
+
19
+ def mode=(mode)
20
+ @mode =
21
+ if MODES.include?(mode)
22
+ mode
23
+ else
24
+ :read
25
+ end
26
+ end
27
+
28
+ def write(force_mode = true)
29
+ if force_mode && reading?
30
+ self.mode = :write
31
+ elsif !force_mode && reading?
32
+ fail 'You chose to write the buffer when in read mode'
33
+ end
34
+
35
+ @size = @data.length
36
+ "#{@size}-#{@data}"
37
+ end
38
+
39
+ def <<(data)
40
+ @data << data
41
+ if reading?
42
+ get_data_size
43
+ check_for_flush
44
+ end
45
+ end
46
+
47
+ def set_data(data) # rubocop:disable Style/AccessorMethodName
48
+ @data = data.to_s
49
+ @size = @data.size
50
+ end
51
+
52
+ def reading?
53
+ mode == :read
54
+ end
55
+
56
+ def writing?
57
+ mode == :write
58
+ end
59
+
60
+ def flushed?
61
+ @flush
62
+ end
63
+
64
+ def get_data_size # rubocop:disable Style/AccessorMethodName
65
+ if @size == 0 || @data.match(SIZE_REGEX)
66
+ sliced_size = @data.slice!(SIZE_REGEX)
67
+ @size = sliced_size.delete('-').to_i unless sliced_size.nil?
68
+ end
69
+ end
70
+
71
+ private
72
+
73
+ def check_for_flush
74
+ @flush = true if !@size.nil? && @data.length == @size
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,140 @@
1
+ require 'forwardable'
2
+ require 'protobuf'
3
+ require 'protobuf/logging'
4
+ require 'protobuf/rpc/error'
5
+
6
+ module Protobuf
7
+ module Rpc
8
+ class Client
9
+ extend Forwardable
10
+ include Protobuf::Logging
11
+
12
+ def_delegators :@connector, :options, :complete_cb, :success_cb, :failure_cb, :send_request
13
+ attr_reader :connector
14
+
15
+ # Create a new client with default options (defined in ClientConnection)
16
+ # See Service#client for a more convenient way to create a client, as well
17
+ # as Client#method_missing defined below.
18
+ #
19
+ # request = WidgetFindRequest.new
20
+ # client = Client.new({
21
+ # :service => WidgetService,
22
+ # :method => "find",
23
+ # :request_type => "WidgetFindRequest",
24
+ # :response_type => "WidgetList",
25
+ # :request => request
26
+ # })
27
+ #
28
+ def initialize(options = {})
29
+ fail "Invalid client configuration. Service must be defined." if options[:service].nil?
30
+ @connector = ::Protobuf.connector_type_class.new(options)
31
+ logger.debug { sign_message("Initialized with options: #{options.inspect}") }
32
+ end
33
+
34
+ def log_signature
35
+ @_log_signature ||= "[client-#{self.class}]"
36
+ end
37
+
38
+ # Set a complete callback on the client to return the object (self).
39
+ #
40
+ # client = Client.new(:service => WidgetService)
41
+ # client.on_complete {|obj| ... }
42
+ #
43
+ def on_complete(&complete_cb)
44
+ @connector.complete_cb = complete_cb
45
+ end
46
+
47
+ def on_complete=(callable)
48
+ if !callable.nil? && !callable.respond_to?(:call) && callable.arity != 1
49
+ fail "callable must take a single argument and respond to :call"
50
+ end
51
+
52
+ @connector.complete_cb = callable
53
+ end
54
+
55
+ # Set a failure callback on the client to return the
56
+ # error returned by the service, if any. If this callback
57
+ # is called, success_cb will NOT be called.
58
+ #
59
+ # client = Client.new(:service => WidgetService)
60
+ # client.on_failure {|err| ... }
61
+ #
62
+ def on_failure(&failure_cb)
63
+ @connector.failure_cb = failure_cb
64
+ end
65
+
66
+ def on_failure=(callable)
67
+ if !callable.nil? && !callable.respond_to?(:call) && callable.arity != 1
68
+ fail "Callable must take a single argument and respond to :call"
69
+ end
70
+
71
+ @connector.failure_cb = callable
72
+ end
73
+
74
+ # Set a success callback on the client to return the
75
+ # successful response from the service when it is returned.
76
+ # If this callback is called, failure_cb will NOT be called.
77
+ #
78
+ # client = Client.new(:service => WidgetService)
79
+ # client.on_success {|res| ... }
80
+ #
81
+ def on_success(&success_cb)
82
+ @connector.success_cb = success_cb
83
+ end
84
+
85
+ def on_success=(callable)
86
+ if !callable.nil? && !callable.respond_to?(:call) && callable.arity != 1
87
+ fail "Callable must take a single argument and respond to :call"
88
+ end
89
+
90
+ @connector.success_cb = callable
91
+ end
92
+
93
+ # Provides a mechanism to call the service method against the client
94
+ # which will automatically setup the service_class and method_name
95
+ # in the wrapper protobuf request.
96
+ #
97
+ # # The :find method is not defined by Client which will trigger method_missing
98
+ # Client.new(:service => WidgetService).find do |c|
99
+ # # This block will be invoked before the request is made
100
+ # # `c` in this case is the client object you created above
101
+ # c.on_success {|res| ... }
102
+ # c.on_failure {|err| ... }
103
+ # end
104
+ #
105
+ def method_missing(method_name, *params)
106
+ service = options[:service]
107
+ if service.rpc_method?(method_name)
108
+ logger.debug { sign_message("#{service.name}##{method_name}") }
109
+ rpc = service.rpcs[method_name.to_sym]
110
+
111
+ options[:request_type] = rpc.request_type
112
+ logger.debug { sign_message("Request Type: #{options[:request_type].name}") }
113
+
114
+ options[:response_type] = rpc.response_type
115
+ logger.debug { sign_message("Response Type: #{options[:response_type].name}") }
116
+
117
+ options[:method] = method_name.to_s
118
+ options[:request] = params[0].is_a?(Hash) ? options[:request_type].new(params[0]) : params[0]
119
+ logger.debug { sign_message("Request Data: #{options[:request].inspect}") }
120
+
121
+ # Call client to setup on_success and on_failure event callbacks
122
+ if block_given?
123
+ logger.debug { sign_message("client setup callback given, invoking") }
124
+ yield(self)
125
+ else
126
+ logger.debug { sign_message("no block given for callbacks") }
127
+ end
128
+
129
+ send_request
130
+ else
131
+ logger.error { sign_message("#{service.name}##{method_name} not rpc method, passing to super") }
132
+ super(method_name, *params)
133
+ end
134
+ end
135
+
136
+ end
137
+
138
+ ActiveSupport.run_load_hooks(:protobuf_rpc_client, Client)
139
+ end
140
+ end
@@ -0,0 +1,221 @@
1
+ require 'timeout'
2
+ require 'protobuf/logging'
3
+ require 'protobuf/rpc/rpc.pb'
4
+ require 'protobuf/rpc/buffer'
5
+ require 'protobuf/rpc/error'
6
+ require 'protobuf/rpc/stat'
7
+
8
+ module Protobuf
9
+ module Rpc
10
+ module Connectors
11
+ DEFAULT_OPTIONS = {
12
+ :service => nil, # Fully-qualified Service class
13
+ :method => nil, # Service method to invoke
14
+ :host => '127.0.0.1', # The hostname or address of the service (usually overridden)
15
+ :port => '9399', # The port of the service (usually overridden or pre-configured)
16
+ :request => nil, # The request object sent by the client
17
+ :request_type => nil, # The request type expected by the client
18
+ :response_type => nil, # The response type expected by the client
19
+ :timeout => nil, # The timeout for the request, also handled by client.rb
20
+ :client_host => nil, # The hostname or address of this client
21
+ :first_alive_load_balance => false, # Do we want to use check_avail frames before request
22
+ }.freeze
23
+
24
+ class Base
25
+ include Protobuf::Logging
26
+
27
+ attr_reader :options, :error
28
+ attr_accessor :success_cb, :failure_cb, :complete_cb, :stats
29
+
30
+ def initialize(options)
31
+ @options = DEFAULT_OPTIONS.merge(options)
32
+ @stats = ::Protobuf::Rpc::Stat.new(:CLIENT)
33
+ end
34
+
35
+ def any_callbacks?
36
+ [@complete_cb, @failure_cb, @success_cb].any?
37
+ end
38
+
39
+ def close_connection
40
+ fail 'If you inherit a Connector from Base you must implement close_connection'
41
+ end
42
+
43
+ def complete
44
+ @stats.stop
45
+ logger.info { @stats.to_s }
46
+ logger.debug { sign_message('Response proceessing complete') }
47
+ @complete_cb.call(self) unless @complete_cb.nil?
48
+ rescue => e
49
+ logger.error { sign_message('Complete callback error encountered') }
50
+ log_exception(e)
51
+ raise
52
+ end
53
+
54
+ def data_callback(data)
55
+ logger.debug { sign_message('Using data_callback') }
56
+ @used_data_callback = true
57
+ @data = data
58
+ end
59
+
60
+ # All failures should be routed through this method.
61
+ #
62
+ # @param [Symbol] code The code we're using (see ::Protobuf::Socketrpc::ErrorReason)
63
+ # @param [String] message The error message
64
+ def failure(code, message)
65
+ @error = ClientError.new
66
+ @stats.status = @error.code = ::Protobuf::Socketrpc::ErrorReason.fetch(code)
67
+ @error.message = message
68
+
69
+ logger.debug { sign_message("Server failed request (invoking on_failure): #{@error.inspect}") }
70
+
71
+ @failure_cb.call(@error) unless @failure_cb.nil?
72
+ rescue => e
73
+ logger.error { sign_message("Failure callback error encountered") }
74
+ log_exception(e)
75
+ raise
76
+ ensure
77
+ complete
78
+ end
79
+
80
+ def first_alive_load_balance?
81
+ ENV.key?("PB_FIRST_ALIVE_LOAD_BALANCE") ||
82
+ options[:first_alive_load_balance]
83
+ end
84
+
85
+ def initialize_stats
86
+ @stats = ::Protobuf::Rpc::Stat.new(:CLIENT)
87
+ @stats.server = [@options[:port], @options[:host]]
88
+ @stats.service = @options[:service].name
89
+ @stats.method_name = @options[:method].to_s
90
+ rescue => ex
91
+ log_exception(ex)
92
+ failure(:RPC_ERROR, "Invalid stats configuration. #{ex.message}")
93
+ end
94
+
95
+ def log_signature
96
+ @_log_signature ||= "[client-#{self.class}]"
97
+ end
98
+
99
+ def parse_response
100
+ # Close up the connection as we no longer need it
101
+ close_connection
102
+
103
+ logger.debug { sign_message("Parsing response from server (connection closed)") }
104
+
105
+ # Parse out the raw response
106
+ @stats.response_size = @response_data.size unless @response_data.nil?
107
+ response_wrapper = ::Protobuf::Socketrpc::Response.decode(@response_data)
108
+ @stats.server = response_wrapper.server if response_wrapper.field?(:server)
109
+
110
+ # Determine success or failure based on parsed data
111
+ if response_wrapper.field?(:error_reason)
112
+ logger.debug { sign_message("Error response parsed") }
113
+
114
+ # fail the call if we already know the client is failed
115
+ # (don't try to parse out the response payload)
116
+ failure(response_wrapper.error_reason, response_wrapper.error)
117
+ else
118
+ logger.debug { sign_message("Successful response parsed") }
119
+
120
+ # Ensure client_response is an instance
121
+ parsed = @options[:response_type].decode(response_wrapper.response_proto.to_s)
122
+
123
+ if parsed.nil? && !response_wrapper.field?(:error_reason)
124
+ failure(:BAD_RESPONSE_PROTO, 'Unable to parse response from server')
125
+ else
126
+ verify_callbacks
127
+ succeed(parsed)
128
+ return @data if @used_data_callback
129
+ end
130
+ end
131
+ end
132
+
133
+ def ping_port
134
+ @ping_port ||= ENV["PB_RPC_PING_PORT"]
135
+ end
136
+
137
+ def ping_port_enabled?
138
+ ENV.key?("PB_RPC_PING_PORT")
139
+ end
140
+
141
+ def request_bytes
142
+ validate_request_type!
143
+ return ::Protobuf::Socketrpc::Request.encode(request_fields)
144
+ rescue => e
145
+ failure(:INVALID_REQUEST_PROTO, "Could not set request proto: #{e.message}")
146
+ end
147
+
148
+ def request_caller
149
+ @options[:client_host] || ::Protobuf.client_host
150
+ end
151
+
152
+ def request_fields
153
+ { :service_name => @options[:service].name,
154
+ :method_name => @options[:method].to_s,
155
+ :request_proto => @options[:request],
156
+ :caller => request_caller }
157
+ end
158
+
159
+ def send_request
160
+ fail 'If you inherit a Connector from Base you must implement send_request'
161
+ end
162
+
163
+ def setup_connection
164
+ initialize_stats
165
+ @request_data = request_bytes
166
+ @stats.request_size = @request_data.size
167
+ end
168
+
169
+ def succeed(response)
170
+ logger.debug { sign_message("Server succeeded request (invoking on_success)") }
171
+ @success_cb.call(response) unless @success_cb.nil?
172
+ rescue => e
173
+ logger.error { sign_message("Success callback error encountered") }
174
+ log_exception(e)
175
+ failure(:RPC_ERROR, "An exception occurred while calling on_success: #{e.message}")
176
+ ensure
177
+ complete
178
+ end
179
+
180
+ def timeout
181
+ if options[:timeout]
182
+ options[:timeout]
183
+ else
184
+ 300 # seconds
185
+ end
186
+ end
187
+
188
+ # Wrap the given block in a timeout of the configured number of seconds.
189
+ #
190
+ def timeout_wrap(&block)
191
+ ::Timeout.timeout(timeout, &block)
192
+ rescue ::Timeout::Error
193
+ failure(:RPC_FAILED, "The server took longer than #{timeout} seconds to respond")
194
+ end
195
+
196
+ def validate_request_type!
197
+ unless @options[:request].class == @options[:request_type]
198
+ expected = @options[:request_type].name
199
+ actual = @options[:request].class.name
200
+ failure(:INVALID_REQUEST_PROTO, "Expected request type to be type of #{expected}, got #{actual} instead")
201
+ end
202
+ end
203
+
204
+ def verify_callbacks
205
+ unless any_callbacks?
206
+ logger.debug { sign_message("No callbacks set, using data_callback") }
207
+ @success_cb = @failure_cb = method(:data_callback)
208
+ end
209
+ end
210
+
211
+ def verify_options!
212
+ # Verify the options that are necessary and merge them in
213
+ [:service, :method, :host, :port].each do |opt|
214
+ failure(:RPC_ERROR, "Invalid client connection configuration. #{opt} must be a defined option.") if @options[opt].nil?
215
+ end
216
+ end
217
+
218
+ end
219
+ end
220
+ end
221
+ end