protobuf 1.0.0 → 1.0.1
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/Gemfile.lock +1 -1
- data/README.md +138 -126
- data/bin/rpc_server +2 -2
- data/bin/rprotoc +2 -2
- data/examples/reading_a_message.rb +3 -3
- data/examples/writing_a_message.rb +3 -3
- data/lib/protobuf.rb +3 -0
- data/lib/protobuf/compiler/nodes.rb +1 -1
- data/lib/protobuf/compiler/proto_parser.rb +16 -16
- data/lib/protobuf/compiler/visitors.rb +11 -25
- data/lib/protobuf/descriptor/descriptor_builder.rb +58 -58
- data/lib/protobuf/descriptor/field_descriptor.rb +2 -2
- data/lib/protobuf/ext/eventmachine.rb +16 -0
- data/lib/protobuf/message/decoder.rb +6 -6
- data/lib/protobuf/message/field.rb +14 -14
- data/lib/protobuf/message/message.rb +4 -4
- data/lib/protobuf/rpc/client.rb +42 -183
- data/lib/protobuf/rpc/connector.rb +19 -0
- data/lib/protobuf/rpc/connectors/base.rb +29 -0
- data/lib/protobuf/rpc/connectors/em_client.rb +227 -0
- data/lib/protobuf/rpc/connectors/eventmachine.rb +84 -0
- data/lib/protobuf/rpc/connectors/socket.rb +14 -0
- data/lib/protobuf/rpc/service.rb +4 -4
- data/lib/protobuf/version.rb +1 -1
- data/protobuf.gemspec +3 -3
- data/spec/helper/all.rb +13 -0
- data/spec/helper/server.rb +36 -0
- data/spec/helper/tolerance_matcher.rb +40 -0
- data/spec/spec_helper.rb +3 -5
- data/spec/unit/rpc/client_spec.rb +174 -0
- data/spec/unit/rpc/connector_spec.rb +36 -0
- data/spec/unit/rpc/connectors/base_spec.rb +77 -0
- data/spec/unit/rpc/connectors/eventmachine/client_spec.rb +0 -0
- data/spec/unit/rpc/connectors/eventmachine_spec.rb +0 -0
- data/spec/unit/{server_spec.rb → rpc/server_spec.rb} +0 -0
- data/spec/unit/{service_spec.rb → rpc/service_spec.rb} +0 -0
- metadata +79 -63
- data/lib/protobuf/compiler/template/rpc_bin.erb +0 -4
- data/lib/protobuf/compiler/template/rpc_client.erb +0 -18
- data/lib/protobuf/compiler/template/rpc_service.erb +0 -25
- data/lib/protobuf/rpc/client_connection.rb +0 -225
- data/spec/unit/client_spec.rb +0 -128
@@ -1,18 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
require 'protobuf/rpc/client'
|
3
|
-
require '<%= required_file %>'
|
4
|
-
|
5
|
-
# build request
|
6
|
-
request = <%= message_module %>::<%= request %>.new
|
7
|
-
# TODO: setup a request
|
8
|
-
raise StandardError, 'setup a request'
|
9
|
-
|
10
|
-
# create blunk response
|
11
|
-
response = <%= message_module %>::<%= response %>.new
|
12
|
-
|
13
|
-
# execute rpc
|
14
|
-
Protobuf::Rpc::Client.new('localhost', <%= default_port %>).call :<%= underscore name %>, request, response
|
15
|
-
|
16
|
-
# show response
|
17
|
-
puts response
|
18
|
-
|
@@ -1,25 +0,0 @@
|
|
1
|
-
require 'protobuf/rpc/server'
|
2
|
-
require 'protobuf/rpc/handler'
|
3
|
-
require '<%= required_file %>'
|
4
|
-
|
5
|
-
<%- rpcs.each do |name, request, response| -%>
|
6
|
-
class <%= module_name %>::<%= name %>Handler < Protobuf::Rpc::Handler
|
7
|
-
request <%= module_name %>::<%= request %>
|
8
|
-
response <%= module_name %>::<%= response %>
|
9
|
-
|
10
|
-
def self.process_request(request, response)
|
11
|
-
# TODO: edit this method
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
<%- end -%>
|
16
|
-
class <%= module_name %>::<%= service_name %> < Protobuf::Rpc::Server
|
17
|
-
def setup_handlers
|
18
|
-
@handlers = {
|
19
|
-
<%- rpcs.each do |name, | -%>
|
20
|
-
:<%= underscore name %> => <%= module_name %>::<%= name %>Handler,
|
21
|
-
<%- end -%>
|
22
|
-
}
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
@@ -1,225 +0,0 @@
|
|
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
|
data/spec/unit/client_spec.rb
DELETED
@@ -1,128 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require 'spec/proto/test_service_impl'
|
3
|
-
|
4
|
-
describe Protobuf::Rpc::Client do
|
5
|
-
|
6
|
-
context 'when creating a client from a service' do
|
7
|
-
|
8
|
-
it 'should be able to get a client through the Service#client helper method' do
|
9
|
-
Spec::Proto::TestService.client(:port => 9191).should == Protobuf::Rpc::Client.new(:service => Spec::Proto::TestService, :port => 9191)
|
10
|
-
end
|
11
|
-
|
12
|
-
it "should be able to override a service location's host and port" do
|
13
|
-
Spec::Proto::TestService.located_at 'somewheregreat.com:12345'
|
14
|
-
clean_client = Spec::Proto::TestService.client
|
15
|
-
clean_client.options[:host].should == 'somewheregreat.com'
|
16
|
-
clean_client.options[:port].should == 12345
|
17
|
-
|
18
|
-
updated_client = Spec::Proto::TestService.client(:host => 'amazing.com', :port => 54321)
|
19
|
-
updated_client.options[:host].should == 'amazing.com'
|
20
|
-
updated_client.options[:port].should == 54321
|
21
|
-
end
|
22
|
-
|
23
|
-
it 'should be able to define the syncronicity of the client request' do
|
24
|
-
client = Spec::Proto::TestService.client(:async => false)
|
25
|
-
client.options[:async].should be_false
|
26
|
-
client.do_block.should be_true
|
27
|
-
|
28
|
-
client = Spec::Proto::TestService.client(:async => true)
|
29
|
-
client.options[:async].should be_true
|
30
|
-
client.do_block.should be_false
|
31
|
-
end
|
32
|
-
|
33
|
-
it 'should be able to define which service to create itself for' do
|
34
|
-
client = Protobuf::Rpc::Client.new :service => Spec::Proto::TestService
|
35
|
-
client.options[:service].should == Spec::Proto::TestService
|
36
|
-
end
|
37
|
-
|
38
|
-
it 'should have a hard default for host and port on a service that has not been configured' do
|
39
|
-
reset_service_location Spec::Proto::TestService
|
40
|
-
client = Spec::Proto::TestService.client
|
41
|
-
client.options[:host].should == Protobuf::Rpc::Service::DEFAULT_LOCATION[:host]
|
42
|
-
client.options[:port].should == Protobuf::Rpc::Service::DEFAULT_LOCATION[:port]
|
43
|
-
end
|
44
|
-
|
45
|
-
end
|
46
|
-
|
47
|
-
context 'when calling methods on a service client' do
|
48
|
-
|
49
|
-
# NOTE: we are assuming the service methods are accurately
|
50
|
-
# defined inside spec/proto/test_service.rb,
|
51
|
-
# namely the :find method
|
52
|
-
|
53
|
-
it 'should respond to defined service methods' do
|
54
|
-
client = Spec::Proto::TestService.client
|
55
|
-
client.should_receive(:send_request).and_return(nil)
|
56
|
-
expect { client.find(nil) }.should_not raise_error
|
57
|
-
end
|
58
|
-
|
59
|
-
it 'raises a NameError when accessing a var that does not exist' do
|
60
|
-
pending
|
61
|
-
end
|
62
|
-
|
63
|
-
it 'should be able to set and get local variables within client response blocks' do
|
64
|
-
outer_value = 'OUTER'
|
65
|
-
inner_value = 'INNER'
|
66
|
-
client = Spec::Proto::TestService.client(:async => true)
|
67
|
-
|
68
|
-
EM.should_receive(:reactor_running?).and_return(true)
|
69
|
-
EM.stub!(:schedule) do
|
70
|
-
client.instance_variable_get(:@success_callback).call(inner_value)
|
71
|
-
end
|
72
|
-
|
73
|
-
client.find(nil) do |c|
|
74
|
-
c.on_success do |response|
|
75
|
-
outer_value.should == 'OUTER'
|
76
|
-
outer_value = response
|
77
|
-
end
|
78
|
-
end
|
79
|
-
outer_value.should == inner_value
|
80
|
-
end
|
81
|
-
|
82
|
-
end
|
83
|
-
|
84
|
-
context 'when receiving request objects' do
|
85
|
-
|
86
|
-
it 'should be able to create the correct request object if passed a hash' do
|
87
|
-
client = Spec::Proto::TestService.client
|
88
|
-
client.should_receive(:send_request)
|
89
|
-
client.find({:name => 'Test Name', :active => false})
|
90
|
-
client.options[:request].should be_a Spec::Proto::ResourceFindRequest
|
91
|
-
client.options[:request].name.should == 'Test Name'
|
92
|
-
client.options[:request].active.should == false
|
93
|
-
end
|
94
|
-
|
95
|
-
end
|
96
|
-
|
97
|
-
describe '#synchronize_or_return' do
|
98
|
-
|
99
|
-
context 'when a timeout error occurs' do
|
100
|
-
it 'returns a timeout error' do
|
101
|
-
client = Spec::Proto::TestService.client :timeout => 1
|
102
|
-
client.stub(:ensure_callback).and_return(proc {|err|
|
103
|
-
err.should be_a Protobuf::Rpc::ClientError
|
104
|
-
err.message.should == 'Client timeout of 1 seconds expired'
|
105
|
-
err.code.should == Protobuf::Socketrpc::ErrorReason::RPC_ERROR
|
106
|
-
})
|
107
|
-
Timeout.timeout(2) { client.synchronize_or_return }
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
context 'when any other error occurs' do
|
112
|
-
it 'returns the error' do
|
113
|
-
client = Spec::Proto::TestService.client
|
114
|
-
client.stub(:ensure_callback).and_return(proc {|err|
|
115
|
-
err.should be_a Protobuf::Rpc::ClientError
|
116
|
-
err.message.should == 'Client failed: This is another type of error'
|
117
|
-
err.code.should == Protobuf::Socketrpc::ErrorReason::RPC_ERROR
|
118
|
-
})
|
119
|
-
Timeout.timeout(2) {
|
120
|
-
Timeout.stub(:timeout).and_raise(RuntimeError.new('This is another type of error'))
|
121
|
-
client.synchronize_or_return
|
122
|
-
}
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
end
|
127
|
-
|
128
|
-
end
|