protobuffy 3.6.0 → 4.0.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 (209) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +67 -0
  4. data/.rubocop_todo.yml +145 -0
  5. data/.travis.yml +25 -5
  6. data/CHANGES.md +55 -0
  7. data/CONTRIBUTING.md +1 -1
  8. data/LICENSE.txt +17 -9
  9. data/README.md +13 -12
  10. data/Rakefile +15 -11
  11. data/bin/protoc-gen-ruby +8 -3
  12. data/bin/rpc_server +1 -0
  13. data/examples/lib/example/reverse-client.rb +2 -2
  14. data/install-protobuf.sh +28 -0
  15. data/lib/protobuf.rb +57 -53
  16. data/lib/protobuf/cli.rb +94 -74
  17. data/lib/protobuf/code_generator.rb +60 -9
  18. data/lib/protobuf/decoder.rb +19 -65
  19. data/lib/protobuf/deprecation.rb +117 -0
  20. data/lib/protobuf/descriptors/google/protobuf/compiler/plugin.pb.rb +11 -1
  21. data/lib/protobuf/descriptors/google/protobuf/descriptor.pb.rb +55 -3
  22. data/lib/protobuf/encoder.rb +13 -53
  23. data/lib/protobuf/enum.rb +58 -63
  24. data/lib/protobuf/field.rb +4 -4
  25. data/lib/protobuf/field/base_field.rb +101 -173
  26. data/lib/protobuf/field/bool_field.rb +17 -11
  27. data/lib/protobuf/field/bytes_field.rb +21 -35
  28. data/lib/protobuf/field/double_field.rb +0 -1
  29. data/lib/protobuf/field/enum_field.rb +23 -22
  30. data/lib/protobuf/field/field_array.rb +5 -4
  31. data/lib/protobuf/field/fixed32_field.rb +1 -1
  32. data/lib/protobuf/field/fixed64_field.rb +0 -1
  33. data/lib/protobuf/field/float_field.rb +4 -1
  34. data/lib/protobuf/field/int32_field.rb +0 -1
  35. data/lib/protobuf/field/int64_field.rb +0 -1
  36. data/lib/protobuf/field/integer_field.rb +0 -1
  37. data/lib/protobuf/field/message_field.rb +13 -28
  38. data/lib/protobuf/field/sfixed32_field.rb +0 -1
  39. data/lib/protobuf/field/sfixed64_field.rb +0 -1
  40. data/lib/protobuf/field/signed_integer_field.rb +0 -1
  41. data/lib/protobuf/field/sint32_field.rb +0 -1
  42. data/lib/protobuf/field/sint64_field.rb +0 -1
  43. data/lib/protobuf/field/string_field.rb +2 -4
  44. data/lib/protobuf/field/uint32_field.rb +0 -1
  45. data/lib/protobuf/field/uint64_field.rb +0 -1
  46. data/lib/protobuf/field/varint_field.rb +30 -13
  47. data/lib/protobuf/generators/base.rb +30 -16
  48. data/lib/protobuf/generators/enum_generator.rb +6 -9
  49. data/lib/protobuf/generators/extension_generator.rb +1 -2
  50. data/lib/protobuf/generators/field_generator.rb +25 -13
  51. data/lib/protobuf/generators/file_generator.rb +157 -35
  52. data/lib/protobuf/generators/group_generator.rb +22 -17
  53. data/lib/protobuf/generators/message_generator.rb +13 -14
  54. data/lib/protobuf/generators/option_generator.rb +17 -0
  55. data/lib/protobuf/generators/printable.rb +12 -13
  56. data/lib/protobuf/generators/service_generator.rb +2 -3
  57. data/lib/protobuf/http.rb +2 -2
  58. data/lib/protobuf/lifecycle.rb +20 -33
  59. data/lib/protobuf/logging.rb +39 -0
  60. data/lib/protobuf/message.rb +114 -47
  61. data/lib/protobuf/message/fields.rb +170 -88
  62. data/lib/protobuf/message/serialization.rb +19 -18
  63. data/lib/protobuf/optionable.rb +53 -6
  64. data/lib/protobuf/rpc/buffer.rb +18 -19
  65. data/lib/protobuf/rpc/client.rb +22 -50
  66. data/lib/protobuf/rpc/connectors/base.rb +177 -12
  67. data/lib/protobuf/rpc/connectors/http.rb +14 -9
  68. data/lib/protobuf/rpc/connectors/ping.rb +89 -0
  69. data/lib/protobuf/rpc/connectors/socket.rb +13 -8
  70. data/lib/protobuf/rpc/connectors/zmq.rb +178 -73
  71. data/lib/protobuf/rpc/dynamic_discovery.pb.rb +4 -1
  72. data/lib/protobuf/rpc/env.rb +12 -12
  73. data/lib/protobuf/rpc/error.rb +3 -3
  74. data/lib/protobuf/rpc/error/client_error.rb +4 -4
  75. data/lib/protobuf/rpc/error/server_error.rb +9 -9
  76. data/lib/protobuf/rpc/middleware/exception_handler.rb +6 -2
  77. data/lib/protobuf/rpc/middleware/logger.rb +8 -4
  78. data/lib/protobuf/rpc/middleware/request_decoder.rb +17 -21
  79. data/lib/protobuf/rpc/middleware/response_encoder.rb +22 -27
  80. data/lib/protobuf/rpc/middleware/statsd.rb +3 -3
  81. data/lib/protobuf/rpc/rpc.pb.rb +4 -1
  82. data/lib/protobuf/rpc/server.rb +1 -1
  83. data/lib/protobuf/rpc/servers/http/server.rb +19 -17
  84. data/lib/protobuf/rpc/servers/socket/server.rb +78 -70
  85. data/lib/protobuf/rpc/servers/socket/worker.rb +4 -4
  86. data/lib/protobuf/rpc/servers/socket_runner.rb +27 -15
  87. data/lib/protobuf/rpc/servers/zmq/broker.rb +70 -31
  88. data/lib/protobuf/rpc/servers/zmq/server.rb +55 -47
  89. data/lib/protobuf/rpc/servers/zmq/util.rb +14 -13
  90. data/lib/protobuf/rpc/servers/zmq/worker.rb +16 -16
  91. data/lib/protobuf/rpc/servers/zmq_runner.rb +26 -7
  92. data/lib/protobuf/rpc/service.rb +21 -27
  93. data/lib/protobuf/rpc/service_directory.rb +43 -27
  94. data/lib/protobuf/rpc/service_dispatcher.rb +9 -10
  95. data/lib/protobuf/rpc/service_filters.rb +32 -55
  96. data/lib/protobuf/rpc/stat.rb +4 -8
  97. data/lib/protobuf/socket.rb +1 -2
  98. data/lib/protobuf/tasks/compile.rake +3 -4
  99. data/lib/protobuf/varint.rb +9 -0
  100. data/lib/protobuf/varint_pure.rb +13 -0
  101. data/lib/protobuf/version.rb +1 -1
  102. data/lib/protobuf/zmq.rb +2 -2
  103. data/proto/google/protobuf/descriptor.proto +190 -31
  104. data/protobuffy.gemspec +30 -17
  105. data/spec/benchmark/tasks.rb +27 -19
  106. data/spec/bin/protoc-gen-ruby_spec.rb +11 -6
  107. data/spec/encoding/all_types_spec.rb +96 -84
  108. data/spec/encoding/extreme_values_spec.rb +0 -0
  109. data/spec/functional/class_inheritance_spec.rb +52 -0
  110. data/spec/functional/code_generator_spec.rb +38 -0
  111. data/spec/functional/socket_server_spec.rb +15 -15
  112. data/spec/functional/zmq_server_spec.rb +29 -27
  113. data/spec/lib/protobuf/cli_spec.rb +82 -67
  114. data/spec/lib/protobuf/code_generator_spec.rb +37 -10
  115. data/spec/lib/protobuf/enum_spec.rb +77 -46
  116. data/spec/lib/protobuf/field/bool_field_spec.rb +91 -0
  117. data/spec/lib/protobuf/field/double_field_spec.rb +9 -0
  118. data/spec/lib/protobuf/field/enum_field_spec.rb +26 -0
  119. data/spec/lib/protobuf/field/field_array_spec.rb +69 -0
  120. data/spec/lib/protobuf/field/fixed32_field_spec.rb +7 -0
  121. data/spec/lib/protobuf/field/fixed64_field_spec.rb +7 -0
  122. data/spec/lib/protobuf/field/float_field_spec.rb +90 -0
  123. data/spec/lib/protobuf/field/int32_field_spec.rb +114 -1
  124. data/spec/lib/protobuf/field/int64_field_spec.rb +7 -0
  125. data/spec/lib/protobuf/field/message_field_spec.rb +132 -0
  126. data/spec/lib/protobuf/field/sfixed32_field_spec.rb +9 -0
  127. data/spec/lib/protobuf/field/sfixed64_field_spec.rb +9 -0
  128. data/spec/lib/protobuf/field/sint32_field_spec.rb +9 -0
  129. data/spec/lib/protobuf/field/sint64_field_spec.rb +9 -0
  130. data/spec/lib/protobuf/field/string_field_spec.rb +44 -11
  131. data/spec/lib/protobuf/field/uint32_field_spec.rb +7 -0
  132. data/spec/lib/protobuf/field/uint64_field_spec.rb +7 -0
  133. data/spec/lib/protobuf/field_spec.rb +4 -6
  134. data/spec/lib/protobuf/generators/base_spec.rb +80 -13
  135. data/spec/lib/protobuf/generators/enum_generator_spec.rb +35 -21
  136. data/spec/lib/protobuf/generators/extension_generator_spec.rb +12 -13
  137. data/spec/lib/protobuf/generators/field_generator_spec.rb +73 -21
  138. data/spec/lib/protobuf/generators/file_generator_spec.rb +89 -6
  139. data/spec/lib/protobuf/generators/service_generator_spec.rb +25 -13
  140. data/spec/lib/protobuf/lifecycle_spec.rb +25 -20
  141. data/spec/lib/protobuf/message_spec.rb +578 -79
  142. data/spec/lib/protobuf/optionable_spec.rb +202 -26
  143. data/spec/lib/protobuf/rpc/client_spec.rb +16 -16
  144. data/spec/lib/protobuf/rpc/connectors/base_spec.rb +167 -13
  145. data/spec/lib/protobuf/rpc/connectors/connector_spec.rb +4 -5
  146. data/spec/lib/protobuf/rpc/connectors/http_spec.rb +13 -11
  147. data/spec/lib/protobuf/rpc/connectors/ping_spec.rb +69 -0
  148. data/spec/lib/protobuf/rpc/connectors/socket_spec.rb +6 -7
  149. data/spec/lib/protobuf/rpc/connectors/zmq_spec.rb +35 -52
  150. data/spec/lib/protobuf/rpc/middleware/exception_handler_spec.rb +10 -10
  151. data/spec/lib/protobuf/rpc/middleware/logger_spec.rb +11 -11
  152. data/spec/lib/protobuf/rpc/middleware/request_decoder_spec.rb +23 -23
  153. data/spec/lib/protobuf/rpc/middleware/response_encoder_spec.rb +11 -11
  154. data/spec/lib/protobuf/rpc/middleware/statsd_spec.rb +6 -6
  155. data/spec/lib/protobuf/rpc/servers/http/server_spec.rb +47 -44
  156. data/spec/lib/protobuf/rpc/servers/socket_server_spec.rb +6 -6
  157. data/spec/lib/protobuf/rpc/servers/zmq/server_spec.rb +12 -10
  158. data/spec/lib/protobuf/rpc/servers/zmq/util_spec.rb +11 -11
  159. data/spec/lib/protobuf/rpc/servers/zmq/worker_spec.rb +7 -7
  160. data/spec/lib/protobuf/rpc/service_directory_spec.rb +47 -49
  161. data/spec/lib/protobuf/rpc/service_dispatcher_spec.rb +8 -25
  162. data/spec/lib/protobuf/rpc/service_filters_spec.rb +102 -69
  163. data/spec/lib/protobuf/rpc/service_spec.rb +37 -36
  164. data/spec/lib/protobuf/rpc/stat_spec.rb +7 -9
  165. data/spec/lib/protobuf/varint_spec.rb +29 -0
  166. data/spec/lib/protobuf_spec.rb +55 -28
  167. data/spec/spec_helper.rb +12 -27
  168. data/spec/support/all.rb +0 -1
  169. data/spec/support/packed_field.rb +4 -3
  170. data/spec/support/{test → protos}/all_types.data.bin +0 -0
  171. data/spec/support/{test → protos}/all_types.data.txt +0 -0
  172. data/spec/support/{test → protos}/enum.pb.rb +8 -4
  173. data/spec/support/{test → protos}/enum.proto +4 -1
  174. data/spec/support/{test → protos}/extreme_values.data.bin +0 -0
  175. data/spec/support/protos/google_unittest.bin +0 -0
  176. data/spec/support/protos/google_unittest.pb.rb +798 -0
  177. data/spec/support/{test → protos}/google_unittest.proto +237 -66
  178. data/spec/support/protos/google_unittest_custom_options.bin +0 -0
  179. data/spec/support/protos/google_unittest_custom_options.pb.rb +268 -0
  180. data/spec/support/protos/google_unittest_custom_options.proto +424 -0
  181. data/spec/support/protos/google_unittest_import.pb.rb +55 -0
  182. data/spec/support/{test → protos}/google_unittest_import.proto +19 -10
  183. data/spec/support/protos/google_unittest_import_public.pb.rb +31 -0
  184. data/spec/support/{test → protos}/google_unittest_import_public.proto +8 -5
  185. data/spec/support/{test → protos}/multi_field_extensions.pb.rb +5 -2
  186. data/spec/support/{test → protos}/multi_field_extensions.proto +2 -0
  187. data/spec/support/{test → protos}/resource.pb.rb +47 -11
  188. data/spec/support/{test → protos}/resource.proto +24 -1
  189. data/spec/support/resource_service.rb +23 -0
  190. data/spec/support/server.rb +32 -61
  191. metadata +119 -59
  192. data/lib/protobuf/deprecator.rb +0 -42
  193. data/lib/protobuf/logger.rb +0 -93
  194. data/lib/protobuf/rpc/connector.rb +0 -21
  195. data/lib/protobuf/rpc/connectors/common.rb +0 -172
  196. data/spec/data/data.bin +0 -3
  197. data/spec/data/types.bin +0 -0
  198. data/spec/lib/protobuf/logger_spec.rb +0 -145
  199. data/spec/lib/protobuf/rpc/connector_spec.rb +0 -26
  200. data/spec/lib/protobuf/rpc/connectors/common_spec.rb +0 -170
  201. data/spec/support/test/defaults.pb.rb +0 -25
  202. data/spec/support/test/defaults.proto +0 -9
  203. data/spec/support/test/extended.pb.rb +0 -22
  204. data/spec/support/test/extended.proto +0 -10
  205. data/spec/support/test/google_unittest.pb.rb +0 -543
  206. data/spec/support/test/google_unittest_import.pb.rb +0 -37
  207. data/spec/support/test/google_unittest_import_public.pb.rb +0 -8
  208. data/spec/support/test/resource_service.rb +0 -26
  209. data/spec/support/tolerance_matcher.rb +0 -40
@@ -1,16 +1,15 @@
1
1
  require 'forwardable'
2
2
  require 'protobuf'
3
- require 'protobuf/logger'
3
+ require 'protobuf/logging'
4
4
  require 'protobuf/rpc/error'
5
- require 'protobuf/rpc/connector'
6
5
 
7
6
  module Protobuf
8
7
  module Rpc
9
8
  class Client
10
9
  extend Forwardable
11
- include Protobuf::Logger::LogMethods
10
+ include Protobuf::Logging
12
11
 
13
- def_delegators :@connector, :options, :complete_cb, :success_cb, :failure_cb
12
+ def_delegators :@connector, :options, :complete_cb, :success_cb, :failure_cb, :send_request
14
13
  attr_reader :connector
15
14
 
16
15
  # Create a new client with default options (defined in ClientConnection)
@@ -27,9 +26,9 @@ module Protobuf
27
26
  # })
28
27
  #
29
28
  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}") }
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}") }
33
32
  end
34
33
 
35
34
  def log_signature
@@ -46,8 +45,8 @@ module Protobuf
46
45
  end
47
46
 
48
47
  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"
48
+ if !callable.nil? && !callable.respond_to?(:call) && callable.arity != 1
49
+ fail "callable must take a single argument and respond to :call"
51
50
  end
52
51
 
53
52
  @connector.complete_cb = callable
@@ -65,8 +64,8 @@ module Protobuf
65
64
  end
66
65
 
67
66
  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"
67
+ if !callable.nil? && !callable.respond_to?(:call) && callable.arity != 1
68
+ fail "Callable must take a single argument and respond to :call"
70
69
  end
71
70
 
72
71
  @connector.failure_cb = callable
@@ -84,8 +83,8 @@ module Protobuf
84
83
  end
85
84
 
86
85
  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"
86
+ if !callable.nil? && !callable.respond_to?(:call) && callable.arity != 1
87
+ fail "Callable must take a single argument and respond to :call"
89
88
  end
90
89
 
91
90
  @connector.success_cb = callable
@@ -105,62 +104,35 @@ module Protobuf
105
104
  #
106
105
  def method_missing(method_name, *params)
107
106
  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}") }
107
+ if service.rpc_method?(method_name)
108
+ logger.debug { sign_message("#{service.name}##{method_name}") }
113
109
  rpc = service.rpcs[method_name.to_sym]
114
110
 
115
111
  options[:request_type] = rpc.request_type
116
- log_debug { sign_message("Request Type: #{options[:request_type].name}") }
112
+ logger.debug { sign_message("Request Type: #{options[:request_type].name}") }
117
113
 
118
114
  options[:response_type] = rpc.response_type
119
- log_debug { sign_message("Response Type: #{options[:response_type].name}") }
115
+ logger.debug { sign_message("Response Type: #{options[:response_type].name}") }
120
116
 
121
117
  options[:method] = method_name.to_s
122
118
  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}") }
119
+ logger.debug { sign_message("Request Data: #{options[:request].inspect}") }
124
120
 
125
121
  # Call client to setup on_success and on_failure event callbacks
126
122
  if block_given?
127
- log_debug { sign_message("client setup callback given, invoking") }
123
+ logger.debug { sign_message("client setup callback given, invoking") }
128
124
  yield(self)
129
125
  else
130
- log_debug { sign_message("no block given for callbacks") }
126
+ logger.debug { sign_message("no block given for callbacks") }
131
127
  end
132
128
 
133
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)
134
133
  end
135
134
  end
136
135
 
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
136
  end
165
137
 
166
138
  ActiveSupport.run_load_hooks(:protobuf_rpc_client, Client)
@@ -1,10 +1,9 @@
1
1
  require 'timeout'
2
- require 'protobuf/logger'
2
+ require 'protobuf/logging'
3
3
  require 'protobuf/rpc/rpc.pb'
4
4
  require 'protobuf/rpc/buffer'
5
5
  require 'protobuf/rpc/error'
6
6
  require 'protobuf/rpc/stat'
7
- require 'protobuf/rpc/connectors/common'
8
7
 
9
8
  module Protobuf
10
9
  module Rpc
@@ -17,28 +16,117 @@ module Protobuf
17
16
  :request => nil, # The request object sent by the client
18
17
  :request_type => nil, # The request type expected by the client
19
18
  :response_type => nil, # The response type expected by the client
20
- :timeout => 300, # The default timeout for the request, also handled by client.rb
19
+ :timeout => nil, # The timeout for the request, also handled by client.rb
21
20
  :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
- }
21
+ :first_alive_load_balance => false, # Do we want to use check_avail frames before request
22
+ }.freeze
24
23
 
25
24
  class Base
26
- include Protobuf::Logger::LogMethods
25
+ include Protobuf::Logging
27
26
 
28
- attr_reader :options
29
- attr_accessor :success_cb, :failure_cb, :complete_cb
27
+ attr_reader :options, :error
28
+ attr_accessor :success_cb, :failure_cb, :complete_cb, :stats
30
29
 
31
30
  def initialize(options)
32
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
+ @error.code = ::Protobuf::Socketrpc::ErrorReason.fetch(code)
67
+ @error.message = message
68
+ logger.debug { sign_message("Server failed request (invoking on_failure): #{@error.inspect}") }
69
+
70
+ @stats.failure(code)
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
33
78
  end
34
79
 
35
80
  def first_alive_load_balance?
36
- ENV.has_key?("PB_FIRST_ALIVE_LOAD_BALANCE") ||
81
+ ENV.key?("PB_FIRST_ALIVE_LOAD_BALANCE") ||
37
82
  options[:first_alive_load_balance]
38
83
  end
39
84
 
40
- def send_request
41
- raise 'If you inherit a Connector from Base you must implement send_request'
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
+
109
+ # Determine success or failure based on parsed data
110
+ if response_wrapper.field?(:error_reason)
111
+ logger.debug { sign_message("Error response parsed") }
112
+
113
+ # fail the call if we already know the client is failed
114
+ # (don't try to parse out the response payload)
115
+ failure(response_wrapper.error_reason, response_wrapper.error)
116
+ else
117
+ logger.debug { sign_message("Successful response parsed") }
118
+
119
+ # Ensure client_response is an instance
120
+ parsed = @options[:response_type].decode(response_wrapper.response_proto.to_s)
121
+
122
+ if parsed.nil? && !response_wrapper.field?(:error_reason)
123
+ failure(:BAD_RESPONSE_PROTO, 'Unable to parse response from server')
124
+ else
125
+ verify_callbacks
126
+ succeed(parsed)
127
+ return @data if @used_data_callback
128
+ end
129
+ end
42
130
  end
43
131
 
44
132
  def ping_port
@@ -46,8 +134,85 @@ module Protobuf
46
134
  end
47
135
 
48
136
  def ping_port_enabled?
49
- ENV.has_key?("PB_RPC_PING_PORT")
137
+ ENV.key?("PB_RPC_PING_PORT")
50
138
  end
139
+
140
+ def request_bytes
141
+ validate_request_type!
142
+ fields = { :service_name => @options[:service].name,
143
+ :method_name => @options[:method].to_s,
144
+ :request_proto => @options[:request],
145
+ :caller => request_caller }
146
+
147
+ return ::Protobuf::Socketrpc::Request.encode(fields)
148
+ rescue => e
149
+ failure(:INVALID_REQUEST_PROTO, "Could not set request proto: #{e.message}")
150
+ end
151
+
152
+ def request_caller
153
+ @options[:client_host] || ::Protobuf.client_host
154
+ end
155
+
156
+ def send_request
157
+ fail 'If you inherit a Connector from Base you must implement send_request'
158
+ end
159
+
160
+ def setup_connection
161
+ initialize_stats
162
+ @request_data = request_bytes
163
+ @stats.request_size = @request_data.size
164
+ end
165
+
166
+ def succeed(response)
167
+ logger.debug { sign_message("Server succeeded request (invoking on_success)") }
168
+ @stats.success
169
+ @success_cb.call(response) unless @success_cb.nil?
170
+ rescue => e
171
+ logger.error { sign_message("Success callback error encountered") }
172
+ log_exception(e)
173
+ failure(:RPC_ERROR, "An exception occurred while calling on_success: #{e.message}")
174
+ ensure
175
+ complete
176
+ end
177
+
178
+ def timeout
179
+ if options[:timeout]
180
+ options[:timeout]
181
+ else
182
+ 300 # seconds
183
+ end
184
+ end
185
+
186
+ # Wrap the given block in a timeout of the configured number of seconds.
187
+ #
188
+ def timeout_wrap(&block)
189
+ ::Timeout.timeout(timeout, &block)
190
+ rescue ::Timeout::Error
191
+ failure(:RPC_FAILED, "The server took longer than #{timeout} seconds to respond")
192
+ end
193
+
194
+ def validate_request_type!
195
+ unless @options[:request].class == @options[:request_type]
196
+ expected = @options[:request_type].name
197
+ actual = @options[:request].class.name
198
+ failure(:INVALID_REQUEST_PROTO, "Expected request type to be type of #{expected}, got #{actual} instead")
199
+ end
200
+ end
201
+
202
+ def verify_callbacks
203
+ unless any_callbacks?
204
+ logger.debug { sign_message("No callbacks set, using data_callback") }
205
+ @success_cb = @failure_cb = method(:data_callback)
206
+ end
207
+ end
208
+
209
+ def verify_options!
210
+ # Verify the options that are necessary and merge them in
211
+ [:service, :method, :host, :port].each do |opt|
212
+ failure(:RPC_ERROR, "Invalid client connection configuration. #{opt} must be a defined option.") if @options[opt].nil?
213
+ end
214
+ end
215
+
51
216
  end
52
217
  end
53
218
  end
@@ -6,8 +6,7 @@ module Protobuf
6
6
  module Rpc
7
7
  module Connectors
8
8
  class Http < Base
9
- include Protobuf::Rpc::Connectors::Common
10
- include Protobuf::Logger::LogMethods
9
+ include ::Protobuf::Logging
11
10
 
12
11
  def send_request
13
12
  timeout_wrap do
@@ -23,12 +22,12 @@ module Protobuf
23
22
  # private
24
23
 
25
24
  def close_connection
26
- log_debug { sign_message('Connector closed') }
25
+ logger.debug { sign_message('Connector closed') }
27
26
  end
28
27
 
29
28
  # Method to determine error state, must be used with Connector api
30
29
  def error?
31
- log_debug { sign_message("Error state : #{@error}") }
30
+ logger.debug { sign_message("Error state : #{@error}") }
32
31
  if @error
33
32
  true
34
33
  else
@@ -41,31 +40,37 @@ module Protobuf
41
40
  end
42
41
 
43
42
  def base
44
- options[:base] or ''
43
+ options[:base] || ''
45
44
  end
46
45
 
47
46
  def client
48
47
  @_client ||= Faraday.new(:url => host)
49
48
  end
50
49
 
50
+ def post_init
51
+ send_data unless error?
52
+ rescue => e
53
+ fail(:RPC_ERROR, "Connection error: #{e.message}")
54
+ end
55
+
51
56
  def send_data
52
57
  rpc_request = ::Protobuf::Socketrpc::Request.decode(@request_data)
53
58
 
54
59
  http_response = client.post do |http_request|
55
60
  path_components = [''] + rpc_request[:service_name].split('::') + [rpc_request[:method_name]]
56
- http_request.url base + path_components.map{ |x| CGI::escape(x) }.join('/')
61
+ http_request.url base + path_components.map { |x| CGI.escape(x) }.join('/')
57
62
  http_request.headers['Content-Type'] = 'application/x-protobuf'
58
63
  http_request.headers['X-Protobuf-Caller'] = rpc_request[:caller] || ''
59
64
  http_request.body = rpc_request[:request_proto]
60
65
  end
61
66
 
62
67
  # Server returns protobuf response with no error
63
- if http_response.status == 200 and http_response.headers['x-protobuf-error'].nil?
68
+ if http_response.status == 200 && http_response.headers['x-protobuf-error'].nil?
64
69
  rpc_response = Protobuf::Socketrpc::Response.new(
65
70
  :response_proto => http_response.body
66
71
  )
67
72
  # Server returns protobuf error
68
- elsif http_response.status != 200 and not http_response.headers['x-protobuf-error'].nil?
73
+ elsif http_response.status != 200 && http_response.headers['x-protobuf-error']
69
74
  rpc_response = Protobuf::Socketrpc::Response.new(
70
75
  :response_proto => http_response.body,
71
76
  :error => http_response.headers['x-protobuf-error'],
@@ -80,7 +85,7 @@ module Protobuf
80
85
  )
81
86
  end
82
87
 
83
- @response_data = rpc_response.encode()
88
+ @response_data = rpc_response.encode
84
89
 
85
90
  parse_response
86
91
  end
@@ -0,0 +1,89 @@
1
+ require "socket"
2
+
3
+ module Protobuf
4
+ module Rpc
5
+ module Connectors
6
+ class Ping
7
+ attr_reader :host, :port
8
+
9
+ def initialize(host, port)
10
+ @host = host
11
+ @port = port
12
+ end
13
+
14
+ def online?
15
+ socket = tcp_socket
16
+ socket.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_LINGER, [1, 0].pack('ii'))
17
+
18
+ true
19
+ rescue
20
+ false
21
+ ensure
22
+ begin
23
+ socket && socket.close
24
+ rescue IOError
25
+ nil
26
+ end
27
+ end
28
+
29
+ def timeout
30
+ @timeout ||= begin
31
+ if ::ENV.key?("PB_RPC_PING_PORT_TIMEOUT")
32
+ ::ENV["PB_RPC_PING_PORT_TIMEOUT"].to_f / 1000
33
+ else
34
+ 0.2 # 200 ms
35
+ end
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def tcp_socket
42
+ # Reference: http://stackoverflow.com/a/21014439/1457934
43
+ socket = ::Socket.new(family, ::Socket::SOCK_STREAM, 0)
44
+ socket.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
45
+ socket.connect_nonblock(sockaddr)
46
+ socket
47
+ rescue ::IO::WaitWritable
48
+ # IO.select will block until the socket is writable or the timeout
49
+ # is exceeded - whichever comes first.
50
+ if ::IO.select(nil, [socket], nil, timeout)
51
+ begin
52
+ # Verify there is now a good connection
53
+ socket.connect_nonblock(sockaddr)
54
+ socket
55
+ rescue ::Errno::EISCONN
56
+ # Socket is connected.
57
+ socket
58
+ rescue
59
+ # An unexpected exception was raised - the connection is no good.
60
+ socket.close
61
+ raise
62
+ end
63
+ else
64
+ # IO.select returns nil when the socket is not ready before timeout
65
+ # seconds have elapsed
66
+ socket.close
67
+ raise "Connection Timeout"
68
+ end
69
+ end
70
+
71
+ def family
72
+ @family ||= ::Socket.const_get(addrinfo[0][0])
73
+ end
74
+
75
+ def addrinfo
76
+ @addrinfo ||= ::Socket.getaddrinfo(host, nil)
77
+ end
78
+
79
+ def ip
80
+ @ip ||= addrinfo[0][3]
81
+ end
82
+
83
+ def sockaddr
84
+ @sockaddr ||= ::Socket.pack_sockaddr_in(port, ip)
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end