protobuf 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. data/.gitignore +5 -0
  2. data/Gemfile +3 -0
  3. data/Gemfile.lock +28 -0
  4. data/README.md +216 -0
  5. data/Rakefile +1 -0
  6. data/bin/rpc_server +117 -0
  7. data/bin/rprotoc +46 -0
  8. data/examples/addressbook.pb.rb +55 -0
  9. data/examples/addressbook.proto +24 -0
  10. data/examples/reading_a_message.rb +32 -0
  11. data/examples/writing_a_message.rb +46 -0
  12. data/lib/protobuf.rb +6 -0
  13. data/lib/protobuf/common/exceptions.rb +11 -0
  14. data/lib/protobuf/common/logger.rb +64 -0
  15. data/lib/protobuf/common/util.rb +59 -0
  16. data/lib/protobuf/common/wire_type.rb +10 -0
  17. data/lib/protobuf/compiler/compiler.rb +52 -0
  18. data/lib/protobuf/compiler/nodes.rb +323 -0
  19. data/lib/protobuf/compiler/proto.y +216 -0
  20. data/lib/protobuf/compiler/proto2.ebnf +79 -0
  21. data/lib/protobuf/compiler/proto_parser.rb +1425 -0
  22. data/lib/protobuf/compiler/template/rpc_bin.erb +4 -0
  23. data/lib/protobuf/compiler/template/rpc_client.erb +18 -0
  24. data/lib/protobuf/compiler/template/rpc_service.erb +25 -0
  25. data/lib/protobuf/compiler/template/rpc_service_implementation.erb +42 -0
  26. data/lib/protobuf/compiler/visitors.rb +302 -0
  27. data/lib/protobuf/descriptor/descriptor.proto +286 -0
  28. data/lib/protobuf/descriptor/descriptor.rb +55 -0
  29. data/lib/protobuf/descriptor/descriptor_builder.rb +143 -0
  30. data/lib/protobuf/descriptor/descriptor_proto.rb +138 -0
  31. data/lib/protobuf/descriptor/enum_descriptor.rb +33 -0
  32. data/lib/protobuf/descriptor/field_descriptor.rb +49 -0
  33. data/lib/protobuf/descriptor/file_descriptor.rb +37 -0
  34. data/lib/protobuf/message/decoder.rb +83 -0
  35. data/lib/protobuf/message/encoder.rb +46 -0
  36. data/lib/protobuf/message/enum.rb +62 -0
  37. data/lib/protobuf/message/extend.rb +8 -0
  38. data/lib/protobuf/message/field.rb +701 -0
  39. data/lib/protobuf/message/message.rb +402 -0
  40. data/lib/protobuf/message/protoable.rb +38 -0
  41. data/lib/protobuf/rpc/buffer.rb +74 -0
  42. data/lib/protobuf/rpc/client.rb +268 -0
  43. data/lib/protobuf/rpc/client_connection.rb +225 -0
  44. data/lib/protobuf/rpc/error.rb +34 -0
  45. data/lib/protobuf/rpc/error/client_error.rb +31 -0
  46. data/lib/protobuf/rpc/error/server_error.rb +43 -0
  47. data/lib/protobuf/rpc/rpc.pb.rb +107 -0
  48. data/lib/protobuf/rpc/server.rb +183 -0
  49. data/lib/protobuf/rpc/service.rb +244 -0
  50. data/lib/protobuf/rpc/stat.rb +70 -0
  51. data/lib/protobuf/version.rb +3 -0
  52. data/proto/rpc.proto +73 -0
  53. data/protobuf.gemspec +25 -0
  54. data/script/mk_parser +2 -0
  55. data/spec/functional/embedded_service_spec.rb +7 -0
  56. data/spec/proto/test.pb.rb +31 -0
  57. data/spec/proto/test.proto +31 -0
  58. data/spec/proto/test_service.rb +30 -0
  59. data/spec/proto/test_service_impl.rb +17 -0
  60. data/spec/spec_helper.rb +26 -0
  61. data/spec/unit/client_spec.rb +128 -0
  62. data/spec/unit/common/logger_spec.rb +121 -0
  63. data/spec/unit/enum_spec.rb +13 -0
  64. data/spec/unit/message_spec.rb +67 -0
  65. data/spec/unit/server_spec.rb +27 -0
  66. data/spec/unit/service_spec.rb +75 -0
  67. data/test/check_unbuild.rb +30 -0
  68. data/test/data/data.bin +3 -0
  69. data/test/data/data_source.py +14 -0
  70. data/test/data/types.bin +0 -0
  71. data/test/data/types_source.py +22 -0
  72. data/test/data/unk.png +0 -0
  73. data/test/proto/addressbook.pb.rb +66 -0
  74. data/test/proto/addressbook.proto +33 -0
  75. data/test/proto/addressbook_base.pb.rb +58 -0
  76. data/test/proto/addressbook_base.proto +26 -0
  77. data/test/proto/addressbook_ext.pb.rb +20 -0
  78. data/test/proto/addressbook_ext.proto +6 -0
  79. data/test/proto/collision.pb.rb +17 -0
  80. data/test/proto/collision.proto +5 -0
  81. data/test/proto/ext_collision.pb.rb +24 -0
  82. data/test/proto/ext_collision.proto +8 -0
  83. data/test/proto/ext_range.pb.rb +22 -0
  84. data/test/proto/ext_range.proto +7 -0
  85. data/test/proto/float_default.proto +10 -0
  86. data/test/proto/lowercase.pb.rb +30 -0
  87. data/test/proto/lowercase.proto +9 -0
  88. data/test/proto/merge.pb.rb +39 -0
  89. data/test/proto/merge.proto +15 -0
  90. data/test/proto/nested.pb.rb +30 -0
  91. data/test/proto/nested.proto +9 -0
  92. data/test/proto/optional_field.pb.rb +35 -0
  93. data/test/proto/optional_field.proto +12 -0
  94. data/test/proto/packed.pb.rb +22 -0
  95. data/test/proto/packed.proto +6 -0
  96. data/test/proto/rpc.proto +6 -0
  97. data/test/proto/types.pb.rb +84 -0
  98. data/test/proto/types.proto +37 -0
  99. data/test/test_addressbook.rb +56 -0
  100. data/test/test_compiler.rb +325 -0
  101. data/test/test_descriptor.rb +122 -0
  102. data/test/test_enum_value.rb +41 -0
  103. data/test/test_extension.rb +36 -0
  104. data/test/test_lowercase.rb +11 -0
  105. data/test/test_message.rb +128 -0
  106. data/test/test_optional_field.rb +103 -0
  107. data/test/test_packed_field.rb +40 -0
  108. data/test/test_parse.rb +15 -0
  109. data/test/test_repeated_types.rb +132 -0
  110. data/test/test_serialize.rb +61 -0
  111. data/test/test_standard_message.rb +96 -0
  112. data/test/test_types.rb +226 -0
  113. 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