protobuf 1.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.
- data/.gitignore +5 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +28 -0
- data/README.md +216 -0
- data/Rakefile +1 -0
- data/bin/rpc_server +117 -0
- data/bin/rprotoc +46 -0
- data/examples/addressbook.pb.rb +55 -0
- data/examples/addressbook.proto +24 -0
- data/examples/reading_a_message.rb +32 -0
- data/examples/writing_a_message.rb +46 -0
- data/lib/protobuf.rb +6 -0
- data/lib/protobuf/common/exceptions.rb +11 -0
- data/lib/protobuf/common/logger.rb +64 -0
- data/lib/protobuf/common/util.rb +59 -0
- data/lib/protobuf/common/wire_type.rb +10 -0
- data/lib/protobuf/compiler/compiler.rb +52 -0
- data/lib/protobuf/compiler/nodes.rb +323 -0
- data/lib/protobuf/compiler/proto.y +216 -0
- data/lib/protobuf/compiler/proto2.ebnf +79 -0
- data/lib/protobuf/compiler/proto_parser.rb +1425 -0
- data/lib/protobuf/compiler/template/rpc_bin.erb +4 -0
- data/lib/protobuf/compiler/template/rpc_client.erb +18 -0
- data/lib/protobuf/compiler/template/rpc_service.erb +25 -0
- data/lib/protobuf/compiler/template/rpc_service_implementation.erb +42 -0
- data/lib/protobuf/compiler/visitors.rb +302 -0
- data/lib/protobuf/descriptor/descriptor.proto +286 -0
- data/lib/protobuf/descriptor/descriptor.rb +55 -0
- data/lib/protobuf/descriptor/descriptor_builder.rb +143 -0
- data/lib/protobuf/descriptor/descriptor_proto.rb +138 -0
- data/lib/protobuf/descriptor/enum_descriptor.rb +33 -0
- data/lib/protobuf/descriptor/field_descriptor.rb +49 -0
- data/lib/protobuf/descriptor/file_descriptor.rb +37 -0
- data/lib/protobuf/message/decoder.rb +83 -0
- data/lib/protobuf/message/encoder.rb +46 -0
- data/lib/protobuf/message/enum.rb +62 -0
- data/lib/protobuf/message/extend.rb +8 -0
- data/lib/protobuf/message/field.rb +701 -0
- data/lib/protobuf/message/message.rb +402 -0
- data/lib/protobuf/message/protoable.rb +38 -0
- data/lib/protobuf/rpc/buffer.rb +74 -0
- data/lib/protobuf/rpc/client.rb +268 -0
- data/lib/protobuf/rpc/client_connection.rb +225 -0
- data/lib/protobuf/rpc/error.rb +34 -0
- data/lib/protobuf/rpc/error/client_error.rb +31 -0
- data/lib/protobuf/rpc/error/server_error.rb +43 -0
- data/lib/protobuf/rpc/rpc.pb.rb +107 -0
- data/lib/protobuf/rpc/server.rb +183 -0
- data/lib/protobuf/rpc/service.rb +244 -0
- data/lib/protobuf/rpc/stat.rb +70 -0
- data/lib/protobuf/version.rb +3 -0
- data/proto/rpc.proto +73 -0
- data/protobuf.gemspec +25 -0
- data/script/mk_parser +2 -0
- data/spec/functional/embedded_service_spec.rb +7 -0
- data/spec/proto/test.pb.rb +31 -0
- data/spec/proto/test.proto +31 -0
- data/spec/proto/test_service.rb +30 -0
- data/spec/proto/test_service_impl.rb +17 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/unit/client_spec.rb +128 -0
- data/spec/unit/common/logger_spec.rb +121 -0
- data/spec/unit/enum_spec.rb +13 -0
- data/spec/unit/message_spec.rb +67 -0
- data/spec/unit/server_spec.rb +27 -0
- data/spec/unit/service_spec.rb +75 -0
- data/test/check_unbuild.rb +30 -0
- data/test/data/data.bin +3 -0
- data/test/data/data_source.py +14 -0
- data/test/data/types.bin +0 -0
- data/test/data/types_source.py +22 -0
- data/test/data/unk.png +0 -0
- data/test/proto/addressbook.pb.rb +66 -0
- data/test/proto/addressbook.proto +33 -0
- data/test/proto/addressbook_base.pb.rb +58 -0
- data/test/proto/addressbook_base.proto +26 -0
- data/test/proto/addressbook_ext.pb.rb +20 -0
- data/test/proto/addressbook_ext.proto +6 -0
- data/test/proto/collision.pb.rb +17 -0
- data/test/proto/collision.proto +5 -0
- data/test/proto/ext_collision.pb.rb +24 -0
- data/test/proto/ext_collision.proto +8 -0
- data/test/proto/ext_range.pb.rb +22 -0
- data/test/proto/ext_range.proto +7 -0
- data/test/proto/float_default.proto +10 -0
- data/test/proto/lowercase.pb.rb +30 -0
- data/test/proto/lowercase.proto +9 -0
- data/test/proto/merge.pb.rb +39 -0
- data/test/proto/merge.proto +15 -0
- data/test/proto/nested.pb.rb +30 -0
- data/test/proto/nested.proto +9 -0
- data/test/proto/optional_field.pb.rb +35 -0
- data/test/proto/optional_field.proto +12 -0
- data/test/proto/packed.pb.rb +22 -0
- data/test/proto/packed.proto +6 -0
- data/test/proto/rpc.proto +6 -0
- data/test/proto/types.pb.rb +84 -0
- data/test/proto/types.proto +37 -0
- data/test/test_addressbook.rb +56 -0
- data/test/test_compiler.rb +325 -0
- data/test/test_descriptor.rb +122 -0
- data/test/test_enum_value.rb +41 -0
- data/test/test_extension.rb +36 -0
- data/test/test_lowercase.rb +11 -0
- data/test/test_message.rb +128 -0
- data/test/test_optional_field.rb +103 -0
- data/test/test_packed_field.rb +40 -0
- data/test/test_parse.rb +15 -0
- data/test/test_repeated_types.rb +132 -0
- data/test/test_serialize.rb +61 -0
- data/test/test_standard_message.rb +96 -0
- data/test/test_types.rb +226 -0
- metadata +261 -0
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
require 'eventmachine'
|
|
2
|
+
require 'protobuf/common/logger'
|
|
3
|
+
require 'protobuf/rpc/client_connection'
|
|
4
|
+
require 'protobuf/rpc/buffer'
|
|
5
|
+
require 'protobuf/rpc/error'
|
|
6
|
+
require 'timeout'
|
|
7
|
+
|
|
8
|
+
module Protobuf
|
|
9
|
+
module Rpc
|
|
10
|
+
class Client
|
|
11
|
+
include Protobuf::Logger::LogMethods
|
|
12
|
+
|
|
13
|
+
attr_reader :error, :options, :do_block
|
|
14
|
+
|
|
15
|
+
# Create a new client with default options (defined in ClientConnection)
|
|
16
|
+
# See Service#client for a more convenient way to create a client, as well
|
|
17
|
+
# as Client#method_missing defined below.
|
|
18
|
+
#
|
|
19
|
+
# request = WidgetFindRequest.new
|
|
20
|
+
# client = Client.new({
|
|
21
|
+
# :service => WidgetService,
|
|
22
|
+
# :method => "find",
|
|
23
|
+
# :request_type => "WidgetFindRequest",
|
|
24
|
+
# :response_type => "WidgetList",
|
|
25
|
+
# :request => request
|
|
26
|
+
# })
|
|
27
|
+
#
|
|
28
|
+
def initialize options={}
|
|
29
|
+
raise "Invalid client configuration. Service must be defined." if !options[:service] || options[:service].nil?
|
|
30
|
+
@error = {}
|
|
31
|
+
@options = ClientConnection::DEFAULT_OPTIONS.merge(options)
|
|
32
|
+
@success_callback = nil
|
|
33
|
+
@failure_callback = nil
|
|
34
|
+
@do_block = !@options[:async]
|
|
35
|
+
log_debug '[client] Initialized with options: %s' % @options.inspect
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Set a success callback on the client to return the
|
|
39
|
+
# successful response from the service when it is returned.
|
|
40
|
+
# If this callback is called, failure_callback will NOT be called.
|
|
41
|
+
# Callback is called regardless of :async setting.
|
|
42
|
+
#
|
|
43
|
+
# Client(:service => WidgetService).new.find do |c|
|
|
44
|
+
# c.on_success {|res| ... }
|
|
45
|
+
# end
|
|
46
|
+
#
|
|
47
|
+
def on_success &success_callback
|
|
48
|
+
@success_callback = success_callback
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Set a failure callback on the client to return the
|
|
52
|
+
# error returned by the service, if any. If this callback
|
|
53
|
+
# is called, success_callback will NOT be called.
|
|
54
|
+
# Callback is called regardless of :async setting.
|
|
55
|
+
#
|
|
56
|
+
# Client(:service => WidgetService).new.find do |c|
|
|
57
|
+
# c.on_failure {|err| ... }
|
|
58
|
+
# end
|
|
59
|
+
#
|
|
60
|
+
def on_failure &failure_callback
|
|
61
|
+
@failure_callback = failure_callback
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Provides a mechanism to call the service method against the client
|
|
65
|
+
# and will automatically setup the service_class and method_name
|
|
66
|
+
# in the wrapper protobuf request.
|
|
67
|
+
#
|
|
68
|
+
# # find is not defined by Client which will trigger method_missing
|
|
69
|
+
# Client(:service => WidgetService).new.find
|
|
70
|
+
#
|
|
71
|
+
def method_missing method, *params, &client_callback
|
|
72
|
+
service = @options[:service]
|
|
73
|
+
unless service.rpcs[service].keys.include?(method)
|
|
74
|
+
log_error '[client] %s#%s not rpc method, passing to super' % [service.name, method.to_s]
|
|
75
|
+
super method, *params
|
|
76
|
+
else
|
|
77
|
+
log_debug '[client] %s#%s' % [service.name, method.to_s]
|
|
78
|
+
rpc = service.rpcs[service][method.to_sym]
|
|
79
|
+
@options[:request_type] = rpc.request_type
|
|
80
|
+
log_debug '[client] Request Type: %s' % @options[:request_type].name
|
|
81
|
+
@options[:response_type] = rpc.response_type
|
|
82
|
+
log_debug '[client] Response Type: %s' % @options[:response_type].name
|
|
83
|
+
@options[:method] = method.to_s
|
|
84
|
+
@options[:request] = params[0].is_a?(Hash) ? @options[:request_type].new(params[0]) : params[0]
|
|
85
|
+
log_debug '[client] Request Data: %s' % @options[:request].inspect
|
|
86
|
+
|
|
87
|
+
#### TODO remove first part here once we are able to convert everything to the new event based way of handling success/failure
|
|
88
|
+
unless client_callback.nil?
|
|
89
|
+
if client_callback.arity == 2
|
|
90
|
+
@options[:version] = 1.0
|
|
91
|
+
log_debug '[client] version = 1.0'
|
|
92
|
+
|
|
93
|
+
log_warn deprecation_warning
|
|
94
|
+
STDOUT.puts deprecation_warning unless Protobuf::Logger.configured?
|
|
95
|
+
|
|
96
|
+
on_success {|res| client_callback.call(self, res) }
|
|
97
|
+
on_failure {|err| client_callback.call(self, nil) }
|
|
98
|
+
else
|
|
99
|
+
### TODO Replace block above with this once all client definitions use new event driven approach
|
|
100
|
+
# Call client to setup on_success and on_failure event callbacks
|
|
101
|
+
@options[:version] = 2.0
|
|
102
|
+
log_debug '[client] version = 2.0'
|
|
103
|
+
client_callback.call(self)
|
|
104
|
+
end
|
|
105
|
+
else
|
|
106
|
+
log_debug '[client] no callbacks given'
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
send_request
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Send the request to the service through eventmachine.
|
|
114
|
+
# This method is usually never called directly
|
|
115
|
+
# but is invoked by method_missing (see docs above).
|
|
116
|
+
#
|
|
117
|
+
# request = WidgetFindRequest.new
|
|
118
|
+
# client = Client.new({
|
|
119
|
+
# :service => WidgetService,
|
|
120
|
+
# :method => "find",
|
|
121
|
+
# :request_type => "WidgetFindRequest",
|
|
122
|
+
# :response_type => "WidgetList",
|
|
123
|
+
# :request => request
|
|
124
|
+
# })
|
|
125
|
+
#
|
|
126
|
+
# client.on_success do |res|
|
|
127
|
+
# res.widgets.each{|w| puts w.inspect }
|
|
128
|
+
# end
|
|
129
|
+
#
|
|
130
|
+
# client.on_failure do |err|
|
|
131
|
+
# puts err.message
|
|
132
|
+
# end
|
|
133
|
+
#
|
|
134
|
+
# client.send_request
|
|
135
|
+
#
|
|
136
|
+
def send_request
|
|
137
|
+
Thread.new { EM.run } unless EM.reactor_running?
|
|
138
|
+
|
|
139
|
+
EM.schedule do
|
|
140
|
+
log_debug '[client] Scheduling client connection to be created on next tick'
|
|
141
|
+
connection = ClientConnection.connect @options, &ensure_callback
|
|
142
|
+
connection.on_success &@success_callback unless @success_callback.nil?
|
|
143
|
+
connection.on_failure &ensure_callback
|
|
144
|
+
connection.on_complete { @do_block = false } if @do_block
|
|
145
|
+
log_debug '[client] Connection scheduled'
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
return synchronize_or_return
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Block the client call to send_request from returning if
|
|
152
|
+
# async is false, otherwise do nothing.
|
|
153
|
+
# Simple blocking algorithm to sleep while @do_block is set to true.
|
|
154
|
+
# The eventmachine connection has a registered callback to flip
|
|
155
|
+
# @do_block to false as soon as the response has been received and processed.
|
|
156
|
+
# If any errors are returned an appropriate RPC_ERROR is sent back to the client
|
|
157
|
+
# through the failure callback.
|
|
158
|
+
#
|
|
159
|
+
def synchronize_or_return
|
|
160
|
+
if @do_block
|
|
161
|
+
begin
|
|
162
|
+
Timeout.timeout(@options[:timeout]) do
|
|
163
|
+
sleep 0.5 while @do_block
|
|
164
|
+
true
|
|
165
|
+
end
|
|
166
|
+
rescue => ex
|
|
167
|
+
log_error 'An exception occurred while waiting for server response:'
|
|
168
|
+
log_error ex.message
|
|
169
|
+
log_error ex.backtrace.join("\n")
|
|
170
|
+
|
|
171
|
+
if ex.is_a?(Timeout::Error)
|
|
172
|
+
message = 'Client timeout of %d seconds expired' % @options[:timeout]
|
|
173
|
+
else
|
|
174
|
+
message = 'Client failed: %s' % ex.message
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
err = ClientError.new(Protobuf::Socketrpc::ErrorReason::RPC_ERROR, message)
|
|
178
|
+
ensure_callback.call(err)
|
|
179
|
+
end
|
|
180
|
+
else
|
|
181
|
+
true
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Returns a proc that ensures any errors will be returned to the client
|
|
186
|
+
#
|
|
187
|
+
# If a failure callback was set, just use that as a direct assignment
|
|
188
|
+
# otherwise implement one here that simply throws an exception, since we
|
|
189
|
+
# don't want to swallow the black holes.
|
|
190
|
+
#
|
|
191
|
+
# TODO: remove "else" portion below once 1.0 is gone
|
|
192
|
+
#
|
|
193
|
+
def ensure_callback
|
|
194
|
+
@ensure_callback ||= begin
|
|
195
|
+
if @options[:version] == 2.0
|
|
196
|
+
if @failure_callback.nil?
|
|
197
|
+
cbk = proc do |error|
|
|
198
|
+
raise '%s: %s' % [error.code.name, error.message]
|
|
199
|
+
end
|
|
200
|
+
else
|
|
201
|
+
cbk = @failure_callback
|
|
202
|
+
end
|
|
203
|
+
else
|
|
204
|
+
cbk = proc do |error|
|
|
205
|
+
@error = error
|
|
206
|
+
@failure_callback ? @failure_callback.call(self) : raise('%s: %s' % [error.code.name, error.message])
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
cbk
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# Annoying deprecation warning to print if you are still on v1 DSL
|
|
214
|
+
def deprecation_warning
|
|
215
|
+
%Q{
|
|
216
|
+
##################################################
|
|
217
|
+
# Deprecation Warning - Upgrade Client Callbacks
|
|
218
|
+
# ==============================================
|
|
219
|
+
#
|
|
220
|
+
# You are attempting to use two block arguments (presumably client and response)
|
|
221
|
+
# in your client callback for the call to #{@options[:service].name}.client.#{@options[:method]}.
|
|
222
|
+
#
|
|
223
|
+
# The next version of ruby-protobuf will completely remove the
|
|
224
|
+
# style of client calls that accepts two arguments in favor of a more explicit evented approach.
|
|
225
|
+
#
|
|
226
|
+
# You should refactor the code before upgrading to the next version of this gem. An example of callback style v1:
|
|
227
|
+
#
|
|
228
|
+
# #{@options[:service]}.client.#{@options[:method]}(request) do |client, response|
|
|
229
|
+
# if client.failed?
|
|
230
|
+
# # do something with client.error or client.message
|
|
231
|
+
# else
|
|
232
|
+
# # do something with response
|
|
233
|
+
# end
|
|
234
|
+
# end
|
|
235
|
+
#
|
|
236
|
+
# Refactor the previous example of callback style v1 usage to v2 with the following:
|
|
237
|
+
#
|
|
238
|
+
# #{@options[:service]}.client.#{@options[:method]}(request) do |c|
|
|
239
|
+
# c.on_failure do |error|
|
|
240
|
+
# # do something with error.code or error.message
|
|
241
|
+
# end
|
|
242
|
+
# c.on_success do |response|
|
|
243
|
+
# # do something with response
|
|
244
|
+
# end
|
|
245
|
+
# end
|
|
246
|
+
#
|
|
247
|
+
##################################################
|
|
248
|
+
}
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
# Controller error/failure methods
|
|
252
|
+
# TODO remove these when v1 is gone
|
|
253
|
+
def failed?
|
|
254
|
+
!@error.nil? and !@error[:code].nil?
|
|
255
|
+
end
|
|
256
|
+
def error
|
|
257
|
+
@error[:message] if failed?
|
|
258
|
+
end
|
|
259
|
+
def error_reason
|
|
260
|
+
Protobuf::Socketrpc::ErrorReason.name_by_value(@error[:code]).to_s if failed?
|
|
261
|
+
end
|
|
262
|
+
def error_message
|
|
263
|
+
"%s: %s" % [error_reason, error] if failed?
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
end
|
|
@@ -0,0 +1,225 @@
|
|
|
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
|
+
# Handles client connections to the server
|
|
9
|
+
module Protobuf
|
|
10
|
+
module Rpc
|
|
11
|
+
ClientError = Struct.new("ClientError", :code, :message)
|
|
12
|
+
|
|
13
|
+
class ClientConnection < EM::Connection
|
|
14
|
+
include Protobuf::Logger::LogMethods
|
|
15
|
+
|
|
16
|
+
attr_reader :options, :request, :response
|
|
17
|
+
attr_reader :error, :error_reason, :error_message
|
|
18
|
+
|
|
19
|
+
DEFAULT_OPTIONS = {
|
|
20
|
+
:service => nil, # Service to invoke
|
|
21
|
+
:method => nil, # Service method to call
|
|
22
|
+
:host => 'localhost', # A default host (usually overridden)
|
|
23
|
+
:port => '9938', # A default port (usually overridden)
|
|
24
|
+
:request => nil, # The request object sent by the client
|
|
25
|
+
:request_type => nil, # The request type expected by the client
|
|
26
|
+
:response_type => nil, # The response type expected by the client
|
|
27
|
+
:async => false, # Whether or not to block a client call, this is actually handled by client.rb
|
|
28
|
+
:timeout => 30 # The default timeout for the request, also handled by client.rb
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
# For state tracking
|
|
32
|
+
STATES = {
|
|
33
|
+
:pending => 0,
|
|
34
|
+
:succeeded => 1,
|
|
35
|
+
:failed => 2,
|
|
36
|
+
:completed => 3
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
def self.connect options={}
|
|
40
|
+
options = DEFAULT_OPTIONS.merge(options)
|
|
41
|
+
Protobuf::Logger.debug '[client-cnxn] Connecting to server: %s' % options.inspect
|
|
42
|
+
host = options[:host]
|
|
43
|
+
port = options[:port]
|
|
44
|
+
EventMachine.connect host, port, self, options
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def initialize options={}, &failure_callback
|
|
48
|
+
@failure_callback = failure_callback
|
|
49
|
+
|
|
50
|
+
# Verify the options that are necessary and merge them in
|
|
51
|
+
[:service, :method, :host, :port].each do |opt|
|
|
52
|
+
fail(:RPC_ERROR, "Invalid client connection configuration. #{opt} must be a defined option.") if !options[opt] || options[opt].nil?
|
|
53
|
+
end
|
|
54
|
+
@options = DEFAULT_OPTIONS.merge(options)
|
|
55
|
+
log_debug '[client-cnxn] Client Initialized: %s' % options.inspect
|
|
56
|
+
|
|
57
|
+
@error = ClientError.new
|
|
58
|
+
@success_callback = nil
|
|
59
|
+
@state = STATES[:pending]
|
|
60
|
+
|
|
61
|
+
@stats = Protobuf::Rpc::Stat.new(:CLIENT, true)
|
|
62
|
+
@stats.server = [@options[:port], @options[:host]]
|
|
63
|
+
@stats.service = @options[:service].name
|
|
64
|
+
@stats.method = @options[:method]
|
|
65
|
+
rescue
|
|
66
|
+
fail(:RPC_ERROR, 'Failed to initialize connection: %s' % $!.message) unless failed?
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Success callback registration
|
|
70
|
+
def on_success &success_callback
|
|
71
|
+
@success_callback = success_callback
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Failure callback registration
|
|
75
|
+
def on_failure &failure_callback
|
|
76
|
+
@failure_callback = failure_callback
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Completion callback registration
|
|
80
|
+
def on_complete &complete_callback
|
|
81
|
+
@complete_callback = complete_callback
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Called after the EM.connect
|
|
85
|
+
def connection_completed
|
|
86
|
+
log_debug '[client-cnxn] Established server connection, sending request'
|
|
87
|
+
send_request unless error?
|
|
88
|
+
rescue
|
|
89
|
+
fail(:RPC_ERROR, 'Connection error: %s' % $!.message) unless failed?
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Setup the read buffer for data coming back
|
|
93
|
+
def post_init
|
|
94
|
+
log_debug '[client-cnxn] Post init, new read buffer created'
|
|
95
|
+
@buffer = Protobuf::Rpc::Buffer.new :read
|
|
96
|
+
rescue
|
|
97
|
+
fail(:RPC_ERROR, 'Connection error: %s' % $!.message) unless failed?
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def receive_data data
|
|
101
|
+
log_debug '[client-cnxn] receive_data: %s' % data
|
|
102
|
+
@buffer << data
|
|
103
|
+
parse_response if @buffer.flushed?
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def pending?
|
|
107
|
+
@state == STATES[:pending]
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def succeeded?
|
|
111
|
+
@state == STATES[:succeeded]
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def failed?
|
|
115
|
+
@state == STATES[:failed]
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def completed?
|
|
119
|
+
@state == STATES[:completed]
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
private
|
|
123
|
+
|
|
124
|
+
# Sends the request to the server, invoked by the connection_completed event
|
|
125
|
+
def send_request
|
|
126
|
+
request_wrapper = Protobuf::Socketrpc::Request.new
|
|
127
|
+
request_wrapper.service_name = @options[:service].name
|
|
128
|
+
request_wrapper.method_name = @options[:method].to_s
|
|
129
|
+
|
|
130
|
+
if @options[:request].class == @options[:request_type]
|
|
131
|
+
request_wrapper.request_proto = @options[:request].serialize_to_string
|
|
132
|
+
else
|
|
133
|
+
expected = @options[:request_type].name
|
|
134
|
+
actual = @options[:request].class.name
|
|
135
|
+
fail :INVALID_REQUEST_PROTO, 'Expected request type to be type of %s, got %s instead' % [expected, actual]
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
log_debug '[client-cnxn] Sending Request: %s' % request_wrapper.inspect
|
|
139
|
+
request_buffer = Protobuf::Rpc::Buffer.new(:write, request_wrapper)
|
|
140
|
+
send_data(request_buffer.write)
|
|
141
|
+
@stats.request_size = request_buffer.size
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def parse_response
|
|
145
|
+
# Close up the connection as we no longer need it
|
|
146
|
+
close_connection
|
|
147
|
+
|
|
148
|
+
log_debug '[client-cnxn] Parsing response from server (connection closed)'
|
|
149
|
+
@stats.response_size = @buffer.size
|
|
150
|
+
|
|
151
|
+
# Parse out the raw response
|
|
152
|
+
response_wrapper = Protobuf::Socketrpc::Response.new
|
|
153
|
+
response_wrapper.parse_from_string @buffer.data
|
|
154
|
+
|
|
155
|
+
# Determine success or failure based on parsed data
|
|
156
|
+
if response_wrapper.has_field? :error_reason
|
|
157
|
+
log_debug '[client-cnxn] Error response parsed'
|
|
158
|
+
|
|
159
|
+
# fail the call if we already know the client is failed
|
|
160
|
+
# (don't try to parse out the response payload)
|
|
161
|
+
fail response_wrapper.error_reason, response_wrapper.error
|
|
162
|
+
else
|
|
163
|
+
log_debug '[client-cnxn] Successful response parsed'
|
|
164
|
+
|
|
165
|
+
# Ensure client_response is an instance
|
|
166
|
+
response_type = @options[:response_type].new
|
|
167
|
+
parsed = response_type.parse_from_string(response_wrapper.response_proto.to_s)
|
|
168
|
+
|
|
169
|
+
if parsed.nil? and not response_wrapper.has_field?(:error_reason)
|
|
170
|
+
fail :BAD_RESPONSE_PROTO, 'Unable to parse response from server'
|
|
171
|
+
else
|
|
172
|
+
succeed parsed
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def fail code, message
|
|
178
|
+
@state = STATES[:failed]
|
|
179
|
+
@error.code = code.is_a?(Symbol) ? Protobuf::Socketrpc::ErrorReason.values[code] : code
|
|
180
|
+
@error.message = message
|
|
181
|
+
log_debug '[client-cnxn] Server failed request (invoking on_failure): %s' % @error.inspect
|
|
182
|
+
begin
|
|
183
|
+
@stats.end
|
|
184
|
+
@stats.log_stats
|
|
185
|
+
@failure_callback.call(@error) unless @failure_callback.nil?
|
|
186
|
+
rescue
|
|
187
|
+
log_error '[client-cnxn] Failure callback error encountered: %s' % $!.message
|
|
188
|
+
log_error '[client-cnxn] %s' % $!.backtrace.join("\n")
|
|
189
|
+
raise
|
|
190
|
+
ensure
|
|
191
|
+
complete
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def succeed response
|
|
196
|
+
@state = STATES[:succeeded]
|
|
197
|
+
begin
|
|
198
|
+
log_debug '[client-cnxn] Server succeeded request (invoking on_success)'
|
|
199
|
+
@stats.end
|
|
200
|
+
@stats.log_stats
|
|
201
|
+
@success_callback.call(response) unless @success_callback.nil?
|
|
202
|
+
complete
|
|
203
|
+
rescue
|
|
204
|
+
log_error '[client-cnxn] Success callback error encountered: %s' % $!.message
|
|
205
|
+
log_error '[client-cnxn] %s' % $!.backtrace.join("\n")
|
|
206
|
+
fail :RPC_ERROR, 'An exception occurred while calling on_success: %s' % $!.message
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def complete
|
|
211
|
+
@state = STATES[:completed]
|
|
212
|
+
begin
|
|
213
|
+
log_debug '[client-cnxn] Response proceessing complete'
|
|
214
|
+
@success_callback = @failure_callback = nil
|
|
215
|
+
@complete_callback.call(@state) unless @complete_callback.nil?
|
|
216
|
+
rescue
|
|
217
|
+
log_error '[client-cnxn] Complete callback error encountered: %s' % $!.message
|
|
218
|
+
log_error '[client-cnxn] %s' % $!.backtrace.join("\n")
|
|
219
|
+
raise
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
end
|