msgpack-rpc 0.3.0 → 0.4.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/NOTICE +5 -1
- data/lib/msgpack/rpc.rb +144 -1430
- data/lib/msgpack/rpc/address.rb +144 -0
- data/lib/msgpack/rpc/client.rb +80 -0
- data/lib/msgpack/rpc/dispatcher.rb +111 -0
- data/lib/msgpack/rpc/exception.rb +53 -0
- data/lib/msgpack/rpc/future.rb +116 -0
- data/lib/msgpack/rpc/loop.rb +87 -0
- data/lib/msgpack/rpc/message.rb +31 -0
- data/lib/msgpack/rpc/server.rb +111 -0
- data/lib/msgpack/rpc/session.rb +157 -0
- data/lib/msgpack/rpc/session_pool.rb +91 -0
- data/lib/msgpack/rpc/transport/base.rb +48 -0
- data/lib/msgpack/rpc/transport/tcp.rb +242 -0
- data/lib/msgpack/rpc/transport/udp.rb +192 -0
- data/lib/msgpack/rpc/transport/unix.rb +165 -0
- data/test/test_helper.rb +2 -2
- metadata +17 -3
@@ -0,0 +1,144 @@
|
|
1
|
+
#
|
2
|
+
# MessagePack-RPC for Ruby
|
3
|
+
#
|
4
|
+
# Copyright (C) 2010 FURUHASHI Sadayuki
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
module MessagePack
|
19
|
+
module RPC
|
20
|
+
|
21
|
+
|
22
|
+
class Address
|
23
|
+
# +--+----+
|
24
|
+
# | 2| 4 |
|
25
|
+
# +--+----+
|
26
|
+
# port network byte order
|
27
|
+
# IPv4 address
|
28
|
+
#
|
29
|
+
# +--+----------------+
|
30
|
+
# | 2| 16 |
|
31
|
+
# +--+----------------+
|
32
|
+
# port network byte order
|
33
|
+
# IPv6 address
|
34
|
+
#
|
35
|
+
|
36
|
+
test = Socket.pack_sockaddr_in(0,'0.0.0.0')
|
37
|
+
if test[0] == "\0"[0] || test[1] == "\0"[0]
|
38
|
+
# Linux
|
39
|
+
def initialize(host, port)
|
40
|
+
raw = Socket.pack_sockaddr_in(port, host)
|
41
|
+
family = raw.unpack('S')[0]
|
42
|
+
if family == Socket::AF_INET
|
43
|
+
@serial = raw[2,6]
|
44
|
+
elsif family == Socket::AF_INET6
|
45
|
+
@serial = raw[2,2] + raw[8,16]
|
46
|
+
else
|
47
|
+
raise "Unknown address family: #{family}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
else
|
51
|
+
# BSD
|
52
|
+
def initialize(host, port)
|
53
|
+
raw = Socket.pack_sockaddr_in(port, host)
|
54
|
+
family = raw.unpack('CC')[1]
|
55
|
+
if family == Socket::AF_INET
|
56
|
+
@serial = raw[2,6]
|
57
|
+
elsif family == Socket::AF_INET6
|
58
|
+
@serial = raw[2,2] + raw[8,16]
|
59
|
+
else
|
60
|
+
raise "Unknown address family: #{family}"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def host
|
66
|
+
unpack[0]
|
67
|
+
end
|
68
|
+
|
69
|
+
def port
|
70
|
+
unpack[1]
|
71
|
+
end
|
72
|
+
|
73
|
+
def connectable?
|
74
|
+
port != 0
|
75
|
+
end
|
76
|
+
|
77
|
+
def sockaddr
|
78
|
+
Address.parse_sockaddr(@serial)
|
79
|
+
end
|
80
|
+
|
81
|
+
def unpack
|
82
|
+
Address.parse(@serial)
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.parse_sockaddr(raw)
|
86
|
+
if raw.length == 6
|
87
|
+
addr = Socket.pack_sockaddr_in(0, '0.0.0.0')
|
88
|
+
addr[2,6] = raw[0,6]
|
89
|
+
else
|
90
|
+
addr = Socket.pack_sockaddr_in(0, '::')
|
91
|
+
addr[2,2] = raw[0,2]
|
92
|
+
addr[8,16] = raw[2,16]
|
93
|
+
end
|
94
|
+
addr
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.parse(raw)
|
98
|
+
Socket.unpack_sockaddr_in(parse_sockaddr(raw)).reverse
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.load(raw)
|
102
|
+
Address.new *parse(raw)
|
103
|
+
end
|
104
|
+
|
105
|
+
def dump
|
106
|
+
@serial
|
107
|
+
end
|
108
|
+
|
109
|
+
def to_msgpack(out = '')
|
110
|
+
@serial.to_msgpack(out)
|
111
|
+
end
|
112
|
+
|
113
|
+
def to_s
|
114
|
+
unpack.join(':')
|
115
|
+
end
|
116
|
+
|
117
|
+
def to_a
|
118
|
+
unpack
|
119
|
+
end
|
120
|
+
|
121
|
+
def <=>(o)
|
122
|
+
dump <=> o.dump
|
123
|
+
end
|
124
|
+
|
125
|
+
def inspect
|
126
|
+
"#<#{self.class} #{to_s} @serial=#{@serial.inspect}>"
|
127
|
+
end
|
128
|
+
|
129
|
+
def eql?(o)
|
130
|
+
o.class == Address && dump.eql?(o.dump)
|
131
|
+
end
|
132
|
+
|
133
|
+
def hash
|
134
|
+
dump.hash
|
135
|
+
end
|
136
|
+
|
137
|
+
def ==(o)
|
138
|
+
eql?(o)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
#
|
2
|
+
# MessagePack-RPC for Ruby
|
3
|
+
#
|
4
|
+
# Copyright (C) 2010 FURUHASHI Sadayuki
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
module MessagePack
|
19
|
+
module RPC
|
20
|
+
|
21
|
+
|
22
|
+
# Client is usable for RPC client.
|
23
|
+
# Note that SessionPool includes LoopUtil.
|
24
|
+
class Client < Session
|
25
|
+
# 1. initialize(builder, address, loop = Loop.new)
|
26
|
+
# 2. initialize(host, port, loop = Loop.new)
|
27
|
+
#
|
28
|
+
# Creates a client.
|
29
|
+
def initialize(arg1, arg2, arg3=nil)
|
30
|
+
# 1.
|
31
|
+
if arg1.respond_to?(:build_transport)
|
32
|
+
builder = arg1
|
33
|
+
address = arg2
|
34
|
+
loop = arg3 || Loop.new
|
35
|
+
else
|
36
|
+
# 2.
|
37
|
+
builder = TCPTransport.new
|
38
|
+
address = Address.new(arg1, arg2)
|
39
|
+
loop = arg3 || Loop.new
|
40
|
+
end
|
41
|
+
|
42
|
+
super(builder, address, loop)
|
43
|
+
|
44
|
+
@timer = Timer.new(1, true, &method(:step_timeout))
|
45
|
+
loop.attach(@timer)
|
46
|
+
end
|
47
|
+
|
48
|
+
# call-seq:
|
49
|
+
# Client.open(arg1, arg2, arg3=nil) {|client| }
|
50
|
+
#
|
51
|
+
# Creates a client, call the block and close the client.
|
52
|
+
def self.open(*args, &block)
|
53
|
+
c = new(*args)
|
54
|
+
begin
|
55
|
+
block.call(c)
|
56
|
+
ensure
|
57
|
+
c.close
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def close
|
62
|
+
@timer.detach if @timer.attached?
|
63
|
+
super
|
64
|
+
end
|
65
|
+
|
66
|
+
include LoopUtil
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
#:nodoc:
|
71
|
+
class Client::Base
|
72
|
+
def initialize(*args)
|
73
|
+
@base = Client.new(*args)
|
74
|
+
end
|
75
|
+
attr_reader :base
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
#
|
2
|
+
# MessagePack-RPC for Ruby
|
3
|
+
#
|
4
|
+
# Copyright (C) 2010 FURUHASHI Sadayuki
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
module MessagePack
|
19
|
+
module RPC
|
20
|
+
|
21
|
+
|
22
|
+
class AsyncResult
|
23
|
+
def initialize
|
24
|
+
@responder = nil
|
25
|
+
@sent = false
|
26
|
+
end
|
27
|
+
|
28
|
+
def result(retval, err = nil)
|
29
|
+
unless @sent
|
30
|
+
if @responder
|
31
|
+
@responder.result(retval, err)
|
32
|
+
else
|
33
|
+
@result = [retval, err]
|
34
|
+
end
|
35
|
+
@sent = true
|
36
|
+
end
|
37
|
+
nil
|
38
|
+
end
|
39
|
+
|
40
|
+
def error(err)
|
41
|
+
result(nil, err)
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
|
45
|
+
def set_responder(res) #:nodoc:
|
46
|
+
@responder = res
|
47
|
+
if @sent && @result
|
48
|
+
@responder.result(*@result)
|
49
|
+
@result = nil
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
class ObjectDispatcher
|
56
|
+
def initialize(obj, accept = obj.public_methods)
|
57
|
+
@obj = obj
|
58
|
+
@accept = accept.map {|m| m.is_a?(Integer) ? m : m.to_s }
|
59
|
+
end
|
60
|
+
|
61
|
+
def dispatch_request(session, method, param, responder)
|
62
|
+
begin
|
63
|
+
sent = false
|
64
|
+
early_result = nil
|
65
|
+
result = forward_method(session, method, param) do |result_|
|
66
|
+
unless result_.is_a?(AsyncResult)
|
67
|
+
responder.result(result_)
|
68
|
+
sent = true
|
69
|
+
end
|
70
|
+
early_result = result_
|
71
|
+
end
|
72
|
+
|
73
|
+
# FIXME on NoMethodError
|
74
|
+
# res.error(NO_METHOD_ERROR); return
|
75
|
+
|
76
|
+
# FIXME on ArgumentError
|
77
|
+
# res.error(ArgumentError); return
|
78
|
+
|
79
|
+
rescue
|
80
|
+
responder.error($!.to_s)
|
81
|
+
return
|
82
|
+
end
|
83
|
+
|
84
|
+
if early_result.is_a?(AsyncResult)
|
85
|
+
early_result.set_responder(responder)
|
86
|
+
elsif sent
|
87
|
+
return
|
88
|
+
elsif result.is_a?(AsyncResult)
|
89
|
+
result.set_responder(responder)
|
90
|
+
else
|
91
|
+
responder.result(result)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def dispatch_notify(session, method, param)
|
96
|
+
forward_method(session, method, param)
|
97
|
+
rescue
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
def forward_method(session, method, param, &block)
|
102
|
+
unless @accept.include?(method)
|
103
|
+
raise NoMethodError, "method `#{method}' is not accepted"
|
104
|
+
end
|
105
|
+
@obj.send(method, *param, &block)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
#
|
2
|
+
# MessagePack-RPC for Ruby
|
3
|
+
#
|
4
|
+
# Copyright (C) 2010 FURUHASHI Sadayuki
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
module MessagePack
|
19
|
+
module RPC
|
20
|
+
|
21
|
+
|
22
|
+
class Error < StandardError
|
23
|
+
end
|
24
|
+
|
25
|
+
class RPCError < Error
|
26
|
+
def initialize(msg)
|
27
|
+
super(msg)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class RemoteError < RPCError
|
32
|
+
def initialize(msg, result = nil)
|
33
|
+
super(msg)
|
34
|
+
@result = result
|
35
|
+
end
|
36
|
+
attr_reader :result
|
37
|
+
end
|
38
|
+
|
39
|
+
class TimeoutError < Error
|
40
|
+
def initialize(msg = "request timed out")
|
41
|
+
super
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class ConnectError < TimeoutError
|
46
|
+
def initialize(msg = "connect failed")
|
47
|
+
super
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
#
|
2
|
+
# MessagePack-RPC for Ruby
|
3
|
+
#
|
4
|
+
# Copyright (C) 2010 FURUHASHI Sadayuki
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
module MessagePack
|
19
|
+
module RPC
|
20
|
+
|
21
|
+
|
22
|
+
# Future describes result of remote procedure call that is initially not known,
|
23
|
+
# because it is not yet received.
|
24
|
+
# You can wait and get the result with get method.
|
25
|
+
class Future
|
26
|
+
def initialize(session, loop, callback = nil) #:nodoc:
|
27
|
+
@timeout = session.timeout
|
28
|
+
@loop = loop
|
29
|
+
@callback_handler = callback
|
30
|
+
@error_handler = nil
|
31
|
+
@result_handler = nil
|
32
|
+
@set = false
|
33
|
+
@error = nil
|
34
|
+
@result = nil
|
35
|
+
end
|
36
|
+
attr_reader :loop
|
37
|
+
attr_accessor :result, :error
|
38
|
+
|
39
|
+
# Wait for receiving result of remote procedure call and returns its result.
|
40
|
+
# If the remote method raises error, then this method raises RemoteError.
|
41
|
+
# If the remote procedure call failed with timeout, this method raises TimeoutError.
|
42
|
+
# Otherwise this method returns the result of remote method.
|
43
|
+
def get
|
44
|
+
join
|
45
|
+
if error.nil?
|
46
|
+
if @result_handler
|
47
|
+
return @result_handler.call(@result)
|
48
|
+
else
|
49
|
+
return @result
|
50
|
+
end
|
51
|
+
end
|
52
|
+
if @error_handler
|
53
|
+
return @error_handler.call(self)
|
54
|
+
else
|
55
|
+
raise @error if @error.is_a?(Error)
|
56
|
+
raise RemoteError.new(@error, @result)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Wait for receiving result of remote procedure call.
|
61
|
+
# This method returns self.
|
62
|
+
# If a callback method is attached, it will be called.
|
63
|
+
def join
|
64
|
+
until @set
|
65
|
+
@loop.run_once
|
66
|
+
end
|
67
|
+
self
|
68
|
+
end
|
69
|
+
|
70
|
+
# call-seq:
|
71
|
+
# attach_callback {|future| }
|
72
|
+
#
|
73
|
+
# Attaches a callback method that is called when the result of remote method is received.
|
74
|
+
def attach_callback(proc = nil, &block)
|
75
|
+
@callback_handler = proc || block
|
76
|
+
if @callback_handler.arity == 2
|
77
|
+
# FIXME backward compatibility
|
78
|
+
handler = @callback_handler
|
79
|
+
@callback_handler = Proc.new {|future|
|
80
|
+
handler.call(future.error, future.result)
|
81
|
+
}
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# For IDL
|
86
|
+
def attach_error_handler(proc = nil, &block) #:nodoc:
|
87
|
+
@error_handler = proc || block
|
88
|
+
end
|
89
|
+
|
90
|
+
# For IDL
|
91
|
+
def attach_result_handler(proc = nil, &block) #:nodoc:
|
92
|
+
@result_handler = proc || block
|
93
|
+
end
|
94
|
+
|
95
|
+
def set_result(err, res) #:nodoc:
|
96
|
+
@error = err
|
97
|
+
@result = res
|
98
|
+
if @callback_handler
|
99
|
+
@callback_handler.call(self)
|
100
|
+
end
|
101
|
+
@set = true
|
102
|
+
end
|
103
|
+
|
104
|
+
def step_timeout #:nodoc:
|
105
|
+
if @timeout < 1
|
106
|
+
true
|
107
|
+
else
|
108
|
+
@timeout -= 1
|
109
|
+
false
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
end
|
116
|
+
end
|