protobuffy 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (192) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +21 -0
  3. data/.travis.yml +12 -0
  4. data/.yardopts +5 -0
  5. data/CHANGES.md +261 -0
  6. data/CONTRIBUTING.md +16 -0
  7. data/Gemfile +3 -0
  8. data/LICENSE.txt +14 -0
  9. data/README.md +58 -0
  10. data/Rakefile +61 -0
  11. data/bin/protoc-gen-ruby +17 -0
  12. data/bin/rpc_server +4 -0
  13. data/examples/bin/reverse-client-http +4 -0
  14. data/examples/bin/reverse-client-socket +4 -0
  15. data/examples/bin/reverse-client-zmq +4 -0
  16. data/examples/config.ru +6 -0
  17. data/examples/definitions/example/reverse.proto +12 -0
  18. data/examples/lib/example/reverse-client.rb +23 -0
  19. data/examples/lib/example/reverse-service.rb +9 -0
  20. data/examples/lib/example/reverse.pb.rb +36 -0
  21. data/lib/protobuf.rb +106 -0
  22. data/lib/protobuf/cli.rb +249 -0
  23. data/lib/protobuf/code_generator.rb +41 -0
  24. data/lib/protobuf/decoder.rb +74 -0
  25. data/lib/protobuf/deprecator.rb +42 -0
  26. data/lib/protobuf/descriptors.rb +3 -0
  27. data/lib/protobuf/descriptors/google/protobuf/compiler/plugin.pb.rb +52 -0
  28. data/lib/protobuf/descriptors/google/protobuf/descriptor.pb.rb +249 -0
  29. data/lib/protobuf/encoder.rb +62 -0
  30. data/lib/protobuf/enum.rb +319 -0
  31. data/lib/protobuf/exceptions.rb +9 -0
  32. data/lib/protobuf/field.rb +74 -0
  33. data/lib/protobuf/field/base_field.rb +280 -0
  34. data/lib/protobuf/field/bool_field.rb +53 -0
  35. data/lib/protobuf/field/bytes_field.rb +81 -0
  36. data/lib/protobuf/field/double_field.rb +26 -0
  37. data/lib/protobuf/field/enum_field.rb +57 -0
  38. data/lib/protobuf/field/field_array.rb +86 -0
  39. data/lib/protobuf/field/fixed32_field.rb +25 -0
  40. data/lib/protobuf/field/fixed64_field.rb +29 -0
  41. data/lib/protobuf/field/float_field.rb +38 -0
  42. data/lib/protobuf/field/int32_field.rb +22 -0
  43. data/lib/protobuf/field/int64_field.rb +22 -0
  44. data/lib/protobuf/field/integer_field.rb +24 -0
  45. data/lib/protobuf/field/message_field.rb +66 -0
  46. data/lib/protobuf/field/sfixed32_field.rb +28 -0
  47. data/lib/protobuf/field/sfixed64_field.rb +29 -0
  48. data/lib/protobuf/field/signed_integer_field.rb +30 -0
  49. data/lib/protobuf/field/sint32_field.rb +22 -0
  50. data/lib/protobuf/field/sint64_field.rb +22 -0
  51. data/lib/protobuf/field/string_field.rb +35 -0
  52. data/lib/protobuf/field/uint32_field.rb +22 -0
  53. data/lib/protobuf/field/uint64_field.rb +22 -0
  54. data/lib/protobuf/field/varint_field.rb +68 -0
  55. data/lib/protobuf/generators/base.rb +71 -0
  56. data/lib/protobuf/generators/enum_generator.rb +42 -0
  57. data/lib/protobuf/generators/extension_generator.rb +28 -0
  58. data/lib/protobuf/generators/field_generator.rb +132 -0
  59. data/lib/protobuf/generators/file_generator.rb +140 -0
  60. data/lib/protobuf/generators/group_generator.rb +113 -0
  61. data/lib/protobuf/generators/message_generator.rb +99 -0
  62. data/lib/protobuf/generators/printable.rb +161 -0
  63. data/lib/protobuf/generators/service_generator.rb +27 -0
  64. data/lib/protobuf/http.rb +20 -0
  65. data/lib/protobuf/lifecycle.rb +46 -0
  66. data/lib/protobuf/logger.rb +86 -0
  67. data/lib/protobuf/message.rb +182 -0
  68. data/lib/protobuf/message/fields.rb +122 -0
  69. data/lib/protobuf/message/serialization.rb +84 -0
  70. data/lib/protobuf/optionable.rb +23 -0
  71. data/lib/protobuf/rpc/buffer.rb +79 -0
  72. data/lib/protobuf/rpc/client.rb +168 -0
  73. data/lib/protobuf/rpc/connector.rb +21 -0
  74. data/lib/protobuf/rpc/connectors/base.rb +54 -0
  75. data/lib/protobuf/rpc/connectors/common.rb +172 -0
  76. data/lib/protobuf/rpc/connectors/http.rb +90 -0
  77. data/lib/protobuf/rpc/connectors/socket.rb +73 -0
  78. data/lib/protobuf/rpc/connectors/zmq.rb +205 -0
  79. data/lib/protobuf/rpc/dynamic_discovery.pb.rb +47 -0
  80. data/lib/protobuf/rpc/env.rb +58 -0
  81. data/lib/protobuf/rpc/error.rb +28 -0
  82. data/lib/protobuf/rpc/error/client_error.rb +31 -0
  83. data/lib/protobuf/rpc/error/server_error.rb +43 -0
  84. data/lib/protobuf/rpc/middleware.rb +25 -0
  85. data/lib/protobuf/rpc/middleware/exception_handler.rb +36 -0
  86. data/lib/protobuf/rpc/middleware/logger.rb +91 -0
  87. data/lib/protobuf/rpc/middleware/request_decoder.rb +83 -0
  88. data/lib/protobuf/rpc/middleware/response_encoder.rb +88 -0
  89. data/lib/protobuf/rpc/middleware/runner.rb +18 -0
  90. data/lib/protobuf/rpc/rpc.pb.rb +53 -0
  91. data/lib/protobuf/rpc/server.rb +39 -0
  92. data/lib/protobuf/rpc/servers/http/server.rb +101 -0
  93. data/lib/protobuf/rpc/servers/http_runner.rb +34 -0
  94. data/lib/protobuf/rpc/servers/socket/server.rb +113 -0
  95. data/lib/protobuf/rpc/servers/socket/worker.rb +56 -0
  96. data/lib/protobuf/rpc/servers/socket_runner.rb +34 -0
  97. data/lib/protobuf/rpc/servers/zmq/broker.rb +155 -0
  98. data/lib/protobuf/rpc/servers/zmq/server.rb +313 -0
  99. data/lib/protobuf/rpc/servers/zmq/util.rb +47 -0
  100. data/lib/protobuf/rpc/servers/zmq/worker.rb +105 -0
  101. data/lib/protobuf/rpc/servers/zmq_runner.rb +51 -0
  102. data/lib/protobuf/rpc/service.rb +179 -0
  103. data/lib/protobuf/rpc/service_directory.rb +245 -0
  104. data/lib/protobuf/rpc/service_dispatcher.rb +46 -0
  105. data/lib/protobuf/rpc/service_filters.rb +273 -0
  106. data/lib/protobuf/rpc/stat.rb +148 -0
  107. data/lib/protobuf/socket.rb +22 -0
  108. data/lib/protobuf/tasks.rb +1 -0
  109. data/lib/protobuf/tasks/compile.rake +61 -0
  110. data/lib/protobuf/version.rb +3 -0
  111. data/lib/protobuf/wire_type.rb +10 -0
  112. data/lib/protobuf/zmq.rb +21 -0
  113. data/proto/dynamic_discovery.proto +44 -0
  114. data/proto/google/protobuf/compiler/plugin.proto +147 -0
  115. data/proto/google/protobuf/descriptor.proto +620 -0
  116. data/proto/rpc.proto +62 -0
  117. data/protobuffy.gemspec +37 -0
  118. data/spec/benchmark/tasks.rb +113 -0
  119. data/spec/bin/protoc-gen-ruby_spec.rb +18 -0
  120. data/spec/data/data.bin +3 -0
  121. data/spec/data/types.bin +0 -0
  122. data/spec/encoding/all_types_spec.rb +91 -0
  123. data/spec/encoding/extreme_values_spec.rb +0 -0
  124. data/spec/functional/socket_server_spec.rb +59 -0
  125. data/spec/functional/zmq_server_spec.rb +103 -0
  126. data/spec/lib/protobuf/cli_spec.rb +267 -0
  127. data/spec/lib/protobuf/code_generator_spec.rb +60 -0
  128. data/spec/lib/protobuf/enum_spec.rb +239 -0
  129. data/spec/lib/protobuf/field/int32_field_spec.rb +7 -0
  130. data/spec/lib/protobuf/field/string_field_spec.rb +46 -0
  131. data/spec/lib/protobuf/field_spec.rb +194 -0
  132. data/spec/lib/protobuf/generators/base_spec.rb +87 -0
  133. data/spec/lib/protobuf/generators/enum_generator_spec.rb +68 -0
  134. data/spec/lib/protobuf/generators/extension_generator_spec.rb +43 -0
  135. data/spec/lib/protobuf/generators/field_generator_spec.rb +99 -0
  136. data/spec/lib/protobuf/generators/file_generator_spec.rb +29 -0
  137. data/spec/lib/protobuf/generators/message_generator_spec.rb +0 -0
  138. data/spec/lib/protobuf/generators/service_generator_spec.rb +43 -0
  139. data/spec/lib/protobuf/lifecycle_spec.rb +89 -0
  140. data/spec/lib/protobuf/logger_spec.rb +136 -0
  141. data/spec/lib/protobuf/message_spec.rb +368 -0
  142. data/spec/lib/protobuf/optionable_spec.rb +46 -0
  143. data/spec/lib/protobuf/rpc/client_spec.rb +66 -0
  144. data/spec/lib/protobuf/rpc/connector_spec.rb +26 -0
  145. data/spec/lib/protobuf/rpc/connectors/base_spec.rb +50 -0
  146. data/spec/lib/protobuf/rpc/connectors/common_spec.rb +170 -0
  147. data/spec/lib/protobuf/rpc/connectors/connector_spec.rb +13 -0
  148. data/spec/lib/protobuf/rpc/connectors/http_spec.rb +61 -0
  149. data/spec/lib/protobuf/rpc/connectors/socket_spec.rb +24 -0
  150. data/spec/lib/protobuf/rpc/connectors/zmq_spec.rb +129 -0
  151. data/spec/lib/protobuf/rpc/middleware/exception_handler_spec.rb +62 -0
  152. data/spec/lib/protobuf/rpc/middleware/logger_spec.rb +49 -0
  153. data/spec/lib/protobuf/rpc/middleware/request_decoder_spec.rb +115 -0
  154. data/spec/lib/protobuf/rpc/middleware/response_encoder_spec.rb +75 -0
  155. data/spec/lib/protobuf/rpc/servers/http/server_spec.rb +104 -0
  156. data/spec/lib/protobuf/rpc/servers/socket_server_spec.rb +38 -0
  157. data/spec/lib/protobuf/rpc/servers/zmq/server_spec.rb +41 -0
  158. data/spec/lib/protobuf/rpc/servers/zmq/util_spec.rb +55 -0
  159. data/spec/lib/protobuf/rpc/servers/zmq/worker_spec.rb +35 -0
  160. data/spec/lib/protobuf/rpc/service_directory_spec.rb +295 -0
  161. data/spec/lib/protobuf/rpc/service_dispatcher_spec.rb +52 -0
  162. data/spec/lib/protobuf/rpc/service_filters_spec.rb +484 -0
  163. data/spec/lib/protobuf/rpc/service_spec.rb +161 -0
  164. data/spec/lib/protobuf/rpc/stat_spec.rb +151 -0
  165. data/spec/lib/protobuf_spec.rb +78 -0
  166. data/spec/spec_helper.rb +57 -0
  167. data/spec/support/all.rb +7 -0
  168. data/spec/support/packed_field.rb +22 -0
  169. data/spec/support/server.rb +94 -0
  170. data/spec/support/test/all_types.data.bin +0 -0
  171. data/spec/support/test/all_types.data.txt +119 -0
  172. data/spec/support/test/defaults.pb.rb +25 -0
  173. data/spec/support/test/defaults.proto +9 -0
  174. data/spec/support/test/enum.pb.rb +59 -0
  175. data/spec/support/test/enum.proto +34 -0
  176. data/spec/support/test/extended.pb.rb +22 -0
  177. data/spec/support/test/extended.proto +10 -0
  178. data/spec/support/test/extreme_values.data.bin +0 -0
  179. data/spec/support/test/google_unittest.pb.rb +543 -0
  180. data/spec/support/test/google_unittest.proto +713 -0
  181. data/spec/support/test/google_unittest_import.pb.rb +37 -0
  182. data/spec/support/test/google_unittest_import.proto +64 -0
  183. data/spec/support/test/google_unittest_import_public.pb.rb +8 -0
  184. data/spec/support/test/google_unittest_import_public.proto +38 -0
  185. data/spec/support/test/multi_field_extensions.pb.rb +56 -0
  186. data/spec/support/test/multi_field_extensions.proto +33 -0
  187. data/spec/support/test/resource.pb.rb +117 -0
  188. data/spec/support/test/resource.proto +94 -0
  189. data/spec/support/test/resource_service.rb +26 -0
  190. data/spec/support/test_app_file.rb +2 -0
  191. data/spec/support/tolerance_matcher.rb +40 -0
  192. metadata +367 -0
@@ -0,0 +1,23 @@
1
+ require 'active_support/concern'
2
+
3
+ module Protobuf
4
+ module Optionable
5
+ extend ::ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+ def get_option(name)
9
+ @_optionable_options.try(:[], name)
10
+ end
11
+
12
+ def set_option(name, value = true)
13
+ @_optionable_options ||= {}
14
+ @_optionable_options[name] = value
15
+ end
16
+ end
17
+
18
+ def get_option(name)
19
+ self.class.get_option(name)
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,79 @@
1
+ module Protobuf
2
+ module Rpc
3
+ class Buffer
4
+
5
+ attr_accessor :mode, :data, :size
6
+
7
+ MODES = [:read, :write]
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
+ if MODES.include?(mode)
21
+ @mode = mode
22
+ else
23
+ @mode = :read
24
+ end
25
+ end
26
+
27
+ def write(force_mode=true)
28
+ if force_mode and reading?
29
+ mode = :write
30
+ elsif not force_mode and reading?
31
+ raise = 'You chose to write the buffer when in read mode'
32
+ end
33
+
34
+ @size = @data.length
35
+ "#{@size}-#{@data}"
36
+ end
37
+
38
+ def <<(data)
39
+ @data << data
40
+ if reading?
41
+ get_data_size
42
+ check_for_flush
43
+ end
44
+ end
45
+
46
+ def set_data(data)
47
+ @data = data.to_s
48
+ @size = @data.size
49
+ end
50
+
51
+ def reading?
52
+ mode == :read
53
+ end
54
+
55
+ def writing?
56
+ mode == :write
57
+ end
58
+
59
+ def flushed?
60
+ @flush
61
+ end
62
+
63
+ def get_data_size
64
+ if @size == 0 || @data.match(SIZE_REGEX)
65
+ sliced_size = @data.slice!(SIZE_REGEX)
66
+ @size = sliced_size.gsub('-', '').to_i unless sliced_size.nil?
67
+ end
68
+ end
69
+
70
+ private
71
+
72
+ def check_for_flush
73
+ if !@size.nil? && @data.length == @size
74
+ @flush = true
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,168 @@
1
+ require 'forwardable'
2
+ require 'protobuf'
3
+ require 'protobuf/logger'
4
+ require 'protobuf/rpc/error'
5
+ require 'protobuf/rpc/connector'
6
+
7
+ module Protobuf
8
+ module Rpc
9
+ class Client
10
+ extend Forwardable
11
+ include Protobuf::Logger::LogMethods
12
+
13
+ def_delegators :@connector, :options, :complete_cb, :success_cb, :failure_cb
14
+ attr_reader :connector
15
+
16
+ # Create a new client with default options (defined in ClientConnection)
17
+ # See Service#client for a more convenient way to create a client, as well
18
+ # as Client#method_missing defined below.
19
+ #
20
+ # request = WidgetFindRequest.new
21
+ # client = Client.new({
22
+ # :service => WidgetService,
23
+ # :method => "find",
24
+ # :request_type => "WidgetFindRequest",
25
+ # :response_type => "WidgetList",
26
+ # :request => request
27
+ # })
28
+ #
29
+ def initialize(options = {})
30
+ raise "Invalid client configuration. Service must be defined." if options[:service].nil?
31
+ @connector = Connector.connector_for_client.new(options)
32
+ log_debug { sign_message("Initialized with options: #{options.inspect}") }
33
+ end
34
+
35
+ def log_signature
36
+ @_log_signature ||= "[client-#{self.class}]"
37
+ end
38
+
39
+ # Set a complete callback on the client to return the object (self).
40
+ #
41
+ # client = Client.new(:service => WidgetService)
42
+ # client.on_complete {|obj| ... }
43
+ #
44
+ def on_complete(&complete_cb)
45
+ @connector.complete_cb = complete_cb
46
+ end
47
+
48
+ def on_complete=(callable)
49
+ if callable != nil && !callable.respond_to?(:call) && callable.arity != 1
50
+ raise "callable must take a single argument and respond to :call"
51
+ end
52
+
53
+ @connector.complete_cb = callable
54
+ end
55
+
56
+ # Set a failure callback on the client to return the
57
+ # error returned by the service, if any. If this callback
58
+ # is called, success_cb will NOT be called.
59
+ #
60
+ # client = Client.new(:service => WidgetService)
61
+ # client.on_failure {|err| ... }
62
+ #
63
+ def on_failure(&failure_cb)
64
+ @connector.failure_cb = failure_cb
65
+ end
66
+
67
+ def on_failure=(callable)
68
+ if callable != nil && !callable.respond_to?(:call) && callable.arity != 1
69
+ raise "Callable must take a single argument and respond to :call"
70
+ end
71
+
72
+ @connector.failure_cb = callable
73
+ end
74
+
75
+ # Set a success callback on the client to return the
76
+ # successful response from the service when it is returned.
77
+ # If this callback is called, failure_cb will NOT be called.
78
+ #
79
+ # client = Client.new(:service => WidgetService)
80
+ # client.on_success {|res| ... }
81
+ #
82
+ def on_success(&success_cb)
83
+ @connector.success_cb = success_cb
84
+ end
85
+
86
+ def on_success=(callable)
87
+ if callable != nil && !callable.respond_to?(:call) && callable.arity != 1
88
+ raise "Callable must take a single argument and respond to :call"
89
+ end
90
+
91
+ @connector.success_cb = callable
92
+ end
93
+
94
+ # Provides a mechanism to call the service method against the client
95
+ # which will automatically setup the service_class and method_name
96
+ # in the wrapper protobuf request.
97
+ #
98
+ # # The :find method is not defined by Client which will trigger method_missing
99
+ # Client.new(:service => WidgetService).find do |c|
100
+ # # This block will be invoked before the request is made
101
+ # # `c` in this case is the client object you created above
102
+ # c.on_success {|res| ... }
103
+ # c.on_failure {|err| ... }
104
+ # end
105
+ #
106
+ def method_missing(method_name, *params)
107
+ service = options[:service]
108
+ unless service.rpc_method?(method_name)
109
+ log_error { sign_message("#{service.name}##{method_name.to_s} not rpc method, passing to super") }
110
+ super(method_name, *params)
111
+ else
112
+ log_debug { sign_message("#{service.name}##{method_name.to_s}") }
113
+ rpc = service.rpcs[method_name.to_sym]
114
+
115
+ options[:request_type] = rpc.request_type
116
+ log_debug { sign_message("Request Type: #{options[:request_type].name}") }
117
+
118
+ options[:response_type] = rpc.response_type
119
+ log_debug { sign_message("Response Type: #{options[:response_type].name}") }
120
+
121
+ options[:method] = method_name.to_s
122
+ options[:request] = params[0].is_a?(Hash) ? options[:request_type].new(params[0]) : params[0]
123
+ log_debug { sign_message("Request Data: #{options[:request].inspect}") }
124
+
125
+ # Call client to setup on_success and on_failure event callbacks
126
+ if block_given?
127
+ log_debug { sign_message("client setup callback given, invoking") }
128
+ yield(self)
129
+ else
130
+ log_debug { sign_message("no block given for callbacks") }
131
+ end
132
+
133
+ send_request
134
+ end
135
+ end
136
+
137
+ # Send the request to the service.
138
+ # This method is usually never called directly
139
+ # but is invoked by method_missing (see docs above).
140
+ #
141
+ # request = WidgetFindRequest.new
142
+ # client = Client.new({
143
+ # :service => WidgetService,
144
+ # :method => "find",
145
+ # :request_type => "WidgetFindRequest",
146
+ # :response_type => "WidgetList",
147
+ # :request => request
148
+ # })
149
+ #
150
+ # client.on_success do |res|
151
+ # res.widgets.each{|w| puts w.inspect }
152
+ # end
153
+ #
154
+ # client.on_failure do |err|
155
+ # puts err.message
156
+ # end
157
+ #
158
+ # client.send_request
159
+ #
160
+ def send_request
161
+ @connector.send_request
162
+ end
163
+
164
+ end
165
+
166
+ ActiveSupport.run_load_hooks(:protobuf_rpc_client, Client)
167
+ end
168
+ end
@@ -0,0 +1,21 @@
1
+ require 'protobuf'
2
+
3
+ module Protobuf
4
+ module Rpc
5
+ class Connector
6
+
7
+ # Returns a connector class for the pre-defined connector_type.
8
+ def self.connector_for_client
9
+ case ::Protobuf.connector_type
10
+ when :zmq then
11
+ ::Protobuf::Rpc::Connectors::Zmq
12
+ when :http then
13
+ ::Protobuf::Rpc::Connectors::Http
14
+ else
15
+ ::Protobuf::Rpc::Connectors::Socket
16
+ end
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,54 @@
1
+ require 'timeout'
2
+ require 'protobuf/logger'
3
+ require 'protobuf/rpc/rpc.pb'
4
+ require 'protobuf/rpc/buffer'
5
+ require 'protobuf/rpc/error'
6
+ require 'protobuf/rpc/stat'
7
+ require 'protobuf/rpc/connectors/common'
8
+
9
+ module Protobuf
10
+ module Rpc
11
+ module Connectors
12
+ DEFAULT_OPTIONS = {
13
+ :service => nil, # Fully-qualified Service class
14
+ :method => nil, # Service method to invoke
15
+ :host => '127.0.0.1', # The hostname or address of the service (usually overridden)
16
+ :port => '9399', # The port of the service (usually overridden or pre-configured)
17
+ :request => nil, # The request object sent by the client
18
+ :request_type => nil, # The request type expected by the client
19
+ :response_type => nil, # The response type expected by the client
20
+ :timeout => 300, # The default timeout for the request, also handled by client.rb
21
+ :client_host => nil, # The hostname or address of this client
22
+ :first_alive_load_balance => false, # Do we want to use check_avail frames before request
23
+ }
24
+
25
+ class Base
26
+ include Protobuf::Logger::LogMethods
27
+
28
+ attr_reader :options
29
+ attr_accessor :success_cb, :failure_cb, :complete_cb
30
+
31
+ def initialize(options)
32
+ @options = DEFAULT_OPTIONS.merge(options)
33
+ end
34
+
35
+ def first_alive_load_balance?
36
+ ENV.has_key?("PB_FIRST_ALIVE_LOAD_BALANCE") ||
37
+ options[:first_alive_load_balance]
38
+ end
39
+
40
+ def send_request
41
+ raise 'If you inherit a Connector from Base you must implement send_request'
42
+ end
43
+
44
+ def ping_port
45
+ @ping_port ||= ENV["PB_RPC_PING_PORT"]
46
+ end
47
+
48
+ def ping_port_enabled?
49
+ ENV.has_key?("PB_RPC_PING_PORT")
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,172 @@
1
+ require 'protobuf'
2
+
3
+ module Protobuf
4
+ module Rpc
5
+ module Connectors
6
+ module Common
7
+
8
+ attr_reader :error
9
+
10
+ def any_callbacks?
11
+ return [@complete_cb, @failure_cb, @success_cb].inject(false) do |reduction, cb|
12
+ reduction = (reduction || !cb.nil?)
13
+ end
14
+ end
15
+
16
+ def request_caller
17
+ @options[:client_host] || ::Protobuf.client_host
18
+ end
19
+
20
+ def complete
21
+ @stats.stop
22
+ log_info { @stats.to_s }
23
+ log_debug { sign_message('Response proceessing complete') }
24
+ @complete_cb.call(self) unless @complete_cb.nil?
25
+ rescue => e
26
+ log_error { sign_message('Complete callback error encountered') }
27
+ log_exception(e)
28
+ raise
29
+ end
30
+
31
+ def data_callback(data)
32
+ log_debug { sign_message('Using data_callback') }
33
+ @used_data_callback = true
34
+ @data = data
35
+ end
36
+
37
+ # All failures should be routed through this method.
38
+ #
39
+ # @param [Symbol] code The code we're using (see ::Protobuf::Socketrpc::ErrorReason)
40
+ # @param [String] message The error message
41
+ def fail(code, message)
42
+ @error = ClientError.new
43
+ @error.code = Protobuf::Socketrpc::ErrorReason.fetch(code)
44
+ @error.message = message
45
+ log_debug { sign_message("Server failed request (invoking on_failure): #{@error.inspect}") }
46
+
47
+ @stats.failure(code)
48
+ @failure_cb.call(@error) unless @failure_cb.nil?
49
+ rescue => e
50
+ log_error { sign_message("Failure callback error encountered") }
51
+ log_exception(e)
52
+ raise
53
+ ensure
54
+ complete
55
+ end
56
+
57
+ def initialize_stats
58
+ @stats = Protobuf::Rpc::Stat.new(:CLIENT)
59
+ @stats.server = [@options[:port], @options[:host]]
60
+ @stats.service = @options[:service].name
61
+ @stats.method_name = @options[:method].to_s
62
+ rescue => ex
63
+ log_exception(ex)
64
+ fail(:RPC_ERROR, "Invalid stats configuration. #{ex.message}")
65
+ end
66
+
67
+ def log_signature
68
+ @_log_signature ||= "[client-#{self.class}]"
69
+ end
70
+
71
+ def parse_response
72
+ # Close up the connection as we no longer need it
73
+ close_connection
74
+
75
+ log_debug { sign_message("Parsing response from server (connection closed)") }
76
+
77
+ # Parse out the raw response
78
+ @stats.response_size = @response_data.size unless @response_data.nil?
79
+ response_wrapper = Protobuf::Socketrpc::Response.decode(@response_data)
80
+
81
+ # Determine success or failure based on parsed data
82
+ if response_wrapper.has_field?(:error_reason)
83
+ log_debug { sign_message("Error response parsed") }
84
+
85
+ # fail the call if we already know the client is failed
86
+ # (don't try to parse out the response payload)
87
+ fail(response_wrapper.error_reason, response_wrapper.error)
88
+ else
89
+ log_debug { sign_message("Successful response parsed") }
90
+
91
+ # Ensure client_response is an instance
92
+ parsed = @options[:response_type].decode(response_wrapper.response_proto.to_s)
93
+
94
+ if parsed.nil? and not response_wrapper.has_field?(:error_reason)
95
+ fail(:BAD_RESPONSE_PROTO, 'Unable to parse response from server')
96
+ else
97
+ verify_callbacks
98
+ succeed(parsed)
99
+ return @data if @used_data_callback
100
+ end
101
+ end
102
+ end
103
+
104
+ def post_init
105
+ send_data unless error?
106
+ rescue => e
107
+ fail(:RPC_ERROR, "Connection error: #{e.message}")
108
+ end
109
+
110
+ def request_bytes
111
+ validate_request_type!
112
+ fields = { :service_name => @options[:service].name,
113
+ :method_name => @options[:method].to_s,
114
+ :request_proto => @options[:request],
115
+ :caller => request_caller }
116
+
117
+ return ::Protobuf::Socketrpc::Request.encode(fields)
118
+ rescue => e
119
+ fail(:INVALID_REQUEST_PROTO, "Could not set request proto: #{e.message}")
120
+ end
121
+
122
+ def setup_connection
123
+ initialize_stats
124
+ @request_data = request_bytes
125
+ @stats.request_size = request_bytes.size
126
+ end
127
+
128
+ def succeed(response)
129
+ log_debug { sign_message("Server succeeded request (invoking on_success)") }
130
+ @stats.success
131
+ @success_cb.call(response) unless @success_cb.nil?
132
+ rescue => e
133
+ log_error { sign_message("Success callback error encountered") }
134
+ log_exception(e)
135
+ fail(:RPC_ERROR, "An exception occurred while calling on_success: #{e.message}")
136
+ ensure
137
+ complete
138
+ end
139
+
140
+ # Wrap the given block in a timeout of the configured number of seconds.
141
+ #
142
+ def timeout_wrap(&block)
143
+ ::Timeout.timeout(options[:timeout], &block)
144
+ rescue ::Timeout::Error
145
+ fail(:RPC_FAILED, "The server took longer than #{options[:timeout]} seconds to respond")
146
+ end
147
+
148
+ def validate_request_type!
149
+ unless @options[:request].class == @options[:request_type]
150
+ expected = @options[:request_type].name
151
+ actual = @options[:request].class.name
152
+ fail(:INVALID_REQUEST_PROTO, "Expected request type to be type of #{expected}, got #{actual} instead")
153
+ end
154
+ end
155
+
156
+ def verify_callbacks
157
+ unless any_callbacks?
158
+ log_debug { sign_message("No callbacks set, using data_callback") }
159
+ @success_cb = @failure_cb = self.method(:data_callback)
160
+ end
161
+ end
162
+
163
+ def verify_options!
164
+ # Verify the options that are necessary and merge them in
165
+ [:service, :method, :host, :port].each do |opt|
166
+ fail(:RPC_ERROR, "Invalid client connection configuration. #{opt} must be a defined option.") if @options[opt].nil?
167
+ end
168
+ end
169
+ end
170
+ end
171
+ end
172
+ end