protobuf 1.0.1 → 1.1.0.beta0

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 (45) hide show
  1. data/.gitignore +3 -0
  2. data/.yardopts +5 -0
  3. data/Gemfile.lock +25 -10
  4. data/bin/rpc_server +38 -33
  5. data/lib/protobuf.rb +22 -3
  6. data/lib/protobuf/common/logger.rb +6 -8
  7. data/lib/protobuf/compiler/visitors.rb +8 -9
  8. data/lib/protobuf/descriptor/descriptor_builder.rb +6 -6
  9. data/lib/protobuf/ext/eventmachine.rb +2 -4
  10. data/lib/protobuf/message/message.rb +1 -3
  11. data/lib/protobuf/rpc/buffer.rb +6 -6
  12. data/lib/protobuf/rpc/client.rb +59 -21
  13. data/lib/protobuf/rpc/connector.rb +10 -9
  14. data/lib/protobuf/rpc/connectors/base.rb +23 -8
  15. data/lib/protobuf/rpc/connectors/common.rb +155 -0
  16. data/lib/protobuf/rpc/connectors/em_client.rb +23 -192
  17. data/lib/protobuf/rpc/connectors/eventmachine.rb +36 -44
  18. data/lib/protobuf/rpc/connectors/socket.rb +58 -1
  19. data/lib/protobuf/rpc/error.rb +6 -14
  20. data/lib/protobuf/rpc/server.rb +72 -99
  21. data/lib/protobuf/rpc/servers/evented_runner.rb +32 -0
  22. data/lib/protobuf/rpc/servers/evented_server.rb +29 -0
  23. data/lib/protobuf/rpc/servers/socket_runner.rb +17 -0
  24. data/lib/protobuf/rpc/servers/socket_server.rb +145 -0
  25. data/lib/protobuf/rpc/service.rb +50 -51
  26. data/lib/protobuf/rpc/stat.rb +2 -2
  27. data/lib/protobuf/version.rb +1 -1
  28. data/protobuf.gemspec +9 -4
  29. data/spec/helper/all.rb +1 -7
  30. data/spec/helper/server.rb +45 -5
  31. data/spec/helper/silent_constants.rb +40 -0
  32. data/spec/proto/test_service.rb +0 -1
  33. data/spec/proto/test_service_impl.rb +4 -3
  34. data/spec/spec_helper.rb +19 -6
  35. data/spec/unit/enum_spec.rb +4 -4
  36. data/spec/unit/rpc/client_spec.rb +32 -42
  37. data/spec/unit/rpc/connector_spec.rb +11 -16
  38. data/spec/unit/rpc/connectors/base_spec.rb +14 -3
  39. data/spec/unit/rpc/connectors/common_spec.rb +132 -0
  40. data/spec/unit/rpc/connectors/{eventmachine/client_spec.rb → eventmachine_client_spec.rb} +0 -0
  41. data/spec/unit/rpc/connectors/socket_spec.rb +49 -0
  42. data/spec/unit/rpc/servers/evented_server_spec.rb +18 -0
  43. data/spec/unit/rpc/servers/socket_server_spec.rb +57 -0
  44. metadata +86 -16
  45. data/spec/unit/rpc/server_spec.rb +0 -27
@@ -1,19 +1,20 @@
1
- require 'protobuf/rpc/connectors/eventmachine'
2
- require 'protobuf/rpc/connectors/socket'
3
-
4
1
  module Protobuf
5
2
  module Rpc
6
3
  class Connector
7
4
 
8
- def self.connector_for_platform platform=RUBY_ENGINE
9
- case platform
10
- when /jruby/i
11
- Connectors::Socket
5
+ def self.connector_for_client
6
+ if defined?(Protobuf::ConnectorType)
7
+ case Protobuf::ConnectorType
8
+ when "Socket" then
9
+ ::Protobuf::Rpc::Connectors::Socket
10
+ else
11
+ ::Protobuf::Rpc::Connectors::EventMachine
12
+ end
12
13
  else
13
- Connectors::EventMachine
14
+ ::Protobuf::Rpc::Connectors::EventMachine
14
15
  end
15
16
  end
16
17
 
17
18
  end
18
19
  end
19
- end
20
+ end
@@ -1,22 +1,37 @@
1
1
  require 'protobuf/common/logger'
2
+ require 'protobuf/rpc/rpc.pb'
3
+ require 'protobuf/rpc/buffer'
4
+ require 'protobuf/rpc/error'
5
+ require 'protobuf/rpc/stat'
6
+ require 'protobuf/rpc/connectors/common'
2
7
 
3
8
  module Protobuf
4
9
  module Rpc
5
10
  module Connectors
11
+ DEFAULT_OPTIONS = {
12
+ :service => nil, # Service to invoke
13
+ :method => nil, # Service method to call
14
+ :host => 'localhost', # A default host (usually overridden)
15
+ :port => '9938', # A default port (usually overridden)
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
+ :async => false, # Whether or not to block a client call, this is actually handled by client.rb
20
+ :timeout => 30 # The default timeout for the request, also handled by client.rb
21
+ }
22
+
6
23
  class Base
7
24
  include Protobuf::Logger::LogMethods
8
25
 
9
26
  attr_reader :options
10
- attr_accessor :success_cb, :failure_cb
11
-
12
- def initialize options
13
- @options = options
14
- @success_cb = nil
15
- @failure_cb = nil
27
+ attr_accessor :success_cb, :failure_cb, :complete_cb
28
+
29
+ def initialize(options)
30
+ @options = DEFAULT_OPTIONS.merge(options)
16
31
  end
17
32
 
18
33
  def send_request
19
- raise 'not implemented'
34
+ raise 'If you inherit a Connector from Base you must implement send_request'
20
35
  end
21
36
 
22
37
  def async?
@@ -26,4 +41,4 @@ module Protobuf
26
41
  end
27
42
  end
28
43
  end
29
- end
44
+ end
@@ -0,0 +1,155 @@
1
+ module Protobuf
2
+ module Rpc
3
+ module Connectors
4
+ module Common
5
+
6
+ def any_callbacks?
7
+ return [@complete_cb, @failure_cb, @success_cb].inject(false) do |reduction, cb|
8
+ reduction = (reduction || !cb.nil?)
9
+ end
10
+ end
11
+
12
+ def complete
13
+ @stats.end
14
+ @stats.log_stats
15
+ log_debug "[#{log_signature}] Response proceessing complete"
16
+ @complete_cb.call(self) unless @complete_cb.nil?
17
+ rescue
18
+ log_error "[#{log_signature}] Complete callback error encountered: %s" % $!.message
19
+ log_error "[#{log_signature}] %s" % $!.backtrace.join("\n")
20
+ raise
21
+ end
22
+
23
+ def data_callback(data)
24
+ log_debug "[#{log_signature}] Using data_callback"
25
+ @used_data_callback = true
26
+ @data = data
27
+ end
28
+
29
+ def error
30
+ @error || ClientError.new
31
+ end
32
+
33
+ def fail(code, message)
34
+ error.code = code.is_a?(Symbol) ? Protobuf::Socketrpc::ErrorReason.values[code] : code
35
+ error.message = message
36
+ log_debug "[#{log_signature}] Server failed request (invoking on_failure): %s" % error.inspect
37
+ @failure_cb.call(error) unless @failure_cb.nil?
38
+ rescue
39
+ log_error "[#{log_signature}] Failure callback error encountered: %s" % $!.message
40
+ log_error "[#{log_signature}] %s" % $!.backtrace.join("\n")
41
+ raise
42
+ ensure
43
+ complete
44
+ end
45
+
46
+ def initialize_stats
47
+ @stats = Protobuf::Rpc::Stat.new(:CLIENT, true)
48
+ @stats.server = [@options[:port], @options[:host]]
49
+ @stats.service = @options[:service].name
50
+ @stats.method = @options[:method]
51
+ self
52
+ rescue => ex
53
+ fail(:RPC_ERROR, "Invalid stats configuration. #{ex.message}")
54
+ end
55
+
56
+ def log_signature
57
+ @log_signature ||= "client-#{self.class}"
58
+ end
59
+
60
+ def parse_response
61
+ # Close up the connection as we no longer need it
62
+ close_connection
63
+
64
+ log_debug "[#{log_signature}] Parsing response from server (connection closed)"
65
+ @stats.response_size = @buffer.size
66
+
67
+ # Parse out the raw response
68
+ response_wrapper = Protobuf::Socketrpc::Response.new
69
+ response_wrapper.parse_from_string(@buffer.data)
70
+
71
+ # Determine success or failure based on parsed data
72
+ if response_wrapper.has_field?(:error_reason)
73
+ log_debug "[#{log_signature}] Error response parsed"
74
+
75
+ # fail the call if we already know the client is failed
76
+ # (don't try to parse out the response payload)
77
+ fail(response_wrapper.error_reason, response_wrapper.error)
78
+ else
79
+ log_debug "[#{log_signature}] Successful response parsed"
80
+
81
+ # Ensure client_response is an instance
82
+ response_type = @options[:response_type].new
83
+ parsed = response_type.parse_from_string(response_wrapper.response_proto.to_s)
84
+
85
+ if parsed.nil? and not response_wrapper.has_field?(:error_reason)
86
+ fail(:BAD_RESPONSE_PROTO, 'Unable to parse response from server')
87
+ else
88
+ verify_callbacks
89
+ succeed(parsed)
90
+ return @data if @used_data_callback
91
+ end
92
+ end
93
+ end
94
+
95
+ # Setup the read buffer for data coming back
96
+ def post_init
97
+ # Setup an object for reponses without callbacks
98
+ @data = nil
99
+ log_debug "[#{log_signature}] Post init, new read buffer created"
100
+ @buffer = Protobuf::Rpc::Buffer.new(:read)
101
+ _send_request unless error?
102
+ log_debug "[#{log_signature}] Post init, new read buffer created just sent"
103
+ rescue
104
+ fail(:RPC_ERROR, 'Connection error: %s' % $!.message)
105
+ end
106
+
107
+ # Sends the request to the server, invoked by the connection_completed event
108
+ def _send_request
109
+ request_wrapper = Protobuf::Socketrpc::Request.new
110
+ request_wrapper.service_name = @options[:service].name
111
+ request_wrapper.method_name = @options[:method].to_s
112
+
113
+ if @options[:request].class == @options[:request_type]
114
+ request_wrapper.request_proto = @options[:request].serialize_to_string
115
+ else
116
+ expected = @options[:request_type].name
117
+ actual = @options[:request].class.name
118
+ fail :INVALID_REQUEST_PROTO, 'Expected request type to be type of %s, got %s instead' % [expected, actual]
119
+ end
120
+
121
+ log_debug "[#{log_signature}] Sending Request: %s" % request_wrapper.inspect
122
+ request_buffer = Protobuf::Rpc::Buffer.new(:write, request_wrapper)
123
+ @stats.request_size = request_buffer.size
124
+ send_data(request_buffer.write)
125
+ end
126
+
127
+ def succeed(response)
128
+ log_debug "[#{log_signature}] Server succeeded request (invoking on_success)"
129
+ @success_cb.call(response) unless @success_cb.nil?
130
+ rescue
131
+ log_error "[#{log_signature}] Success callback error encountered: %s" % $!.message
132
+ log_error "[#{log_signature}] %s" % $!.backtrace.join("\n")
133
+ fail :RPC_ERROR, 'An exception occurred while calling on_success: %s' % $!.message
134
+ ensure
135
+ complete
136
+ end
137
+
138
+ def verify_callbacks
139
+ if !any_callbacks?
140
+ log_debug "[#{log_signature}] No callbacks set, using data_callback"
141
+ @success_cb = @failure_cb = self.method(:data_callback)
142
+ end
143
+ end
144
+
145
+ def verify_options
146
+ # Verify the options that are necessary and merge them in
147
+ [:service, :method, :host, :port].each do |opt|
148
+ fail(:RPC_ERROR, "Invalid client connection configuration. #{opt} must be a defined option.") if @options[opt].nil?
149
+ end
150
+ end
151
+
152
+ end
153
+ end
154
+ end
155
+ end
@@ -1,227 +1,58 @@
1
- require 'eventmachine'
2
- require 'protobuf/common/logger'
3
- require 'protobuf/rpc/rpc.pb'
4
- require 'protobuf/rpc/buffer'
5
- require 'protobuf/rpc/error'
6
- require 'protobuf/rpc/stat'
7
-
8
1
  # Handles client connections to the server
9
2
  module Protobuf
10
3
  module Rpc
11
4
  module Connectors
12
- ClientError = Struct.new("ClientError", :code, :message)
13
5
 
14
6
  class EMClient < EM::Connection
15
7
  include Protobuf::Logger::LogMethods
8
+ include Protobuf::Rpc::Connectors::Common
16
9
 
17
10
  attr_reader :options, :request, :response
18
11
  attr_reader :error, :error_reason, :error_message
19
-
20
- DEFAULT_OPTIONS = {
21
- :service => nil, # Service to invoke
22
- :method => nil, # Service method to call
23
- :host => 'localhost', # A default host (usually overridden)
24
- :port => '9938', # A default port (usually overridden)
25
- :request => nil, # The request object sent by the client
26
- :request_type => nil, # The request type expected by the client
27
- :response_type => nil, # The response type expected by the client
28
- :async => false, # Whether or not to block a client call, this is actually handled by client.rb
29
- :timeout => 30 # The default timeout for the request, also handled by client.rb
30
- }
31
-
32
- # For state tracking
33
- STATES = {
34
- :pending => 0,
35
- :succeeded => 1,
36
- :failed => 2,
37
- :completed => 3
38
- }
39
12
 
40
- def self.connect options={}
41
- options = DEFAULT_OPTIONS.merge(options)
42
- Protobuf::Logger.debug '[client-cnxn] Connecting to server: %s' % options.inspect
43
- host = options[:host]
44
- port = options[:port]
45
- EM.connect host, port, self, options
13
+ class << self
14
+
15
+ def connect(options={})
16
+ options = DEFAULT_OPTIONS.merge(options)
17
+ log_debug "[client-#{self}] Connecting to server: %s" % options.inspect
18
+ EM.connect(options[:host], options[:port], self, options)
19
+ end
20
+
46
21
  end
47
-
48
- def initialize options={}, &failure_cb
22
+
23
+ def initialize(options={}, &failure_cb)
49
24
  @failure_cb = failure_cb
50
-
51
- # Verify the options that are necessary and merge them in
52
- [:service, :method, :host, :port].each do |opt|
53
- fail(:RPC_ERROR, "Invalid client connection configuration. #{opt} must be a defined option.") if !options[opt] || options[opt].nil?
54
- end
55
25
  @options = DEFAULT_OPTIONS.merge(options)
56
- log_debug '[client-cnxn] Client Initialized: %s' % options.inspect
57
-
58
- @error = ClientError.new
59
- @success_cb = nil
60
- @state = STATES[:pending]
61
-
62
- @stats = Protobuf::Rpc::Stat.new(:CLIENT, true)
63
- @stats.server = [@options[:port], @options[:host]]
64
- @stats.service = @options[:service].name
65
- @stats.method = @options[:method]
26
+ verify_options
27
+ initialize_stats
28
+
29
+ log_debug "[#{log_signature}] Client Initialized: %s" % options.inspect
66
30
  rescue
67
- fail(:RPC_ERROR, 'Failed to initialize connection: %s' % $!.message) unless failed?
31
+ fail(:RPC_ERROR, 'Failed to initialize connection: %s' % $!.message)
68
32
  end
69
33
 
70
34
  # Success callback registration
71
- def on_success &success_cb
35
+ def on_success(&success_cb)
72
36
  @success_cb = success_cb
73
37
  end
74
38
 
75
39
  # Failure callback registration
76
- def on_failure &failure_cb
40
+ def on_failure(&failure_cb)
77
41
  @failure_cb = failure_cb
78
42
  end
79
43
 
80
44
  # Completion callback registration
81
- def on_complete &complete_cb
45
+ def on_complete(&complete_cb)
82
46
  @complete_cb = complete_cb
83
47
  end
84
48
 
85
- # Called after the EM.connect
86
- def connection_completed
87
- log_debug '[client-cnxn] Established server connection, sending request'
88
- send_request unless error?
89
- rescue
90
- fail(:RPC_ERROR, 'Connection error: %s' % $!.message) unless failed?
91
- end
92
-
93
- # Setup the read buffer for data coming back
94
- def post_init
95
- log_debug '[client-cnxn] Post init, new read buffer created'
96
- @buffer = Protobuf::Rpc::Buffer.new :read
97
- rescue
98
- fail(:RPC_ERROR, 'Connection error: %s' % $!.message) unless failed?
99
- end
100
-
101
- def receive_data data
102
- log_debug '[client-cnxn] receive_data: %s' % data
49
+ def receive_data(data)
50
+ log_debug "[#{log_signature}] receive_data: %s" % data
103
51
  @buffer << data
104
52
  parse_response if @buffer.flushed?
105
53
  end
106
-
107
- def pending?
108
- @state == STATES[:pending]
109
- end
110
-
111
- def succeeded?
112
- @state == STATES[:succeeded]
113
- end
114
-
115
- def failed?
116
- @state == STATES[:failed]
117
- end
118
-
119
- def completed?
120
- @state == STATES[:completed]
121
- end
122
-
123
- private
124
-
125
- # Sends the request to the server, invoked by the connection_completed event
126
- def send_request
127
- request_wrapper = Protobuf::Socketrpc::Request.new
128
- request_wrapper.service_name = @options[:service].name
129
- request_wrapper.method_name = @options[:method].to_s
130
-
131
- if @options[:request].class == @options[:request_type]
132
- request_wrapper.request_proto = @options[:request].serialize_to_string
133
- else
134
- expected = @options[:request_type].name
135
- actual = @options[:request].class.name
136
- fail :INVALID_REQUEST_PROTO, 'Expected request type to be type of %s, got %s instead' % [expected, actual]
137
- end
138
-
139
- log_debug '[client-cnxn] Sending Request: %s' % request_wrapper.inspect
140
- request_buffer = Protobuf::Rpc::Buffer.new(:write, request_wrapper)
141
- send_data(request_buffer.write)
142
- @stats.request_size = request_buffer.size
143
- end
144
-
145
- def parse_response
146
- # Close up the connection as we no longer need it
147
- close_connection
148
-
149
- log_debug '[client-cnxn] Parsing response from server (connection closed)'
150
- @stats.response_size = @buffer.size
151
-
152
- # Parse out the raw response
153
- response_wrapper = Protobuf::Socketrpc::Response.new
154
- response_wrapper.parse_from_string @buffer.data
155
-
156
- # Determine success or failure based on parsed data
157
- if response_wrapper.has_field? :error_reason
158
- log_debug '[client-cnxn] Error response parsed'
159
-
160
- # fail the call if we already know the client is failed
161
- # (don't try to parse out the response payload)
162
- fail response_wrapper.error_reason, response_wrapper.error
163
- else
164
- log_debug '[client-cnxn] Successful response parsed'
165
-
166
- # Ensure client_response is an instance
167
- response_type = @options[:response_type].new
168
- parsed = response_type.parse_from_string(response_wrapper.response_proto.to_s)
169
-
170
- if parsed.nil? and not response_wrapper.has_field?(:error_reason)
171
- fail :BAD_RESPONSE_PROTO, 'Unable to parse response from server'
172
- else
173
- succeed parsed
174
- end
175
- end
176
- end
177
-
178
- def fail code, message
179
- @state = STATES[:failed]
180
- @error.code = code.is_a?(Symbol) ? Protobuf::Socketrpc::ErrorReason.values[code] : code
181
- @error.message = message
182
- log_debug '[client-cnxn] Server failed request (invoking on_failure): %s' % @error.inspect
183
- begin
184
- @stats.end
185
- @stats.log_stats
186
- @failure_cb.call(@error) unless @failure_cb.nil?
187
- rescue
188
- log_error '[client-cnxn] Failure callback error encountered: %s' % $!.message
189
- log_error '[client-cnxn] %s' % $!.backtrace.join("\n")
190
- raise
191
- ensure
192
- complete
193
- end
194
- end
195
-
196
- def succeed response
197
- @state = STATES[:succeeded]
198
- begin
199
- log_debug '[client-cnxn] Server succeeded request (invoking on_success)'
200
- @stats.end
201
- @stats.log_stats
202
- @success_cb.call(response) unless @success_cb.nil?
203
- complete
204
- rescue
205
- log_error '[client-cnxn] Success callback error encountered: %s' % $!.message
206
- log_error '[client-cnxn] %s' % $!.backtrace.join("\n")
207
- fail :RPC_ERROR, 'An exception occurred while calling on_success: %s' % $!.message
208
- end
209
- end
210
-
211
- def complete
212
- @state = STATES[:completed]
213
- begin
214
- log_debug '[client-cnxn] Response proceessing complete'
215
- @success_cb = @failure_cb = nil
216
- @complete_cb.call(@state) unless @complete_cb.nil?
217
- rescue
218
- log_error '[client-cnxn] Complete callback error encountered: %s' % $!.message
219
- log_error '[client-cnxn] %s' % $!.backtrace.join("\n")
220
- raise
221
- end
222
- end
223
-
54
+
224
55
  end
225
56
  end
226
57
  end
227
- end
58
+ end