protobuf 1.0.1 → 1.1.0.beta0

Sign up to get free protection for your applications and to get access to all the features.
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