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