arpie 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README +22 -15
- data/Rakefile +2 -1
- data/lib/arpie/client.rb +1 -0
- data/lib/arpie/protocol.rb +1 -1
- data/lib/arpie/proxy.rb +65 -13
- data/lib/arpie/server.rb +7 -6
- data/tools/benchmark.rb +5 -19
- metadata +13 -4
data/README
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
= What's this?
|
2
2
|
|
3
|
-
Arpie is a end-to-end framework for sending
|
4
|
-
|
5
|
-
|
3
|
+
Arpie is a end-to-end framework for sending protocol-encoded messages over arbitary
|
4
|
+
IO channels, including UNIX/TCP Sockets, Pipes, and pidgeon carriers (depending on
|
5
|
+
your implementation details).
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
Arpie also provides a robust replay-protected RPC framework.
|
8
|
+
|
9
|
+
The Arpie server uses one ruby-thread per client, the client runs entirely in the
|
10
|
+
calling thread; though an example implementation for evented callbacks is provided.
|
9
11
|
|
10
12
|
== Source Code
|
11
13
|
|
@@ -36,7 +38,7 @@ to get the newest version.
|
|
36
38
|
end
|
37
39
|
|
38
40
|
c = Arpie::Client.new(Arpie::MarshalProtocol.new)
|
39
|
-
c.connect do
|
41
|
+
c.connect do
|
40
42
|
TCPSocket.new("127.0.0.1", 51210)
|
41
43
|
end
|
42
44
|
|
@@ -85,14 +87,17 @@ See the Client accessors for modifying this behaviour.
|
|
85
87
|
It is assumed that each call, that is being placed, is atomic - eg, no
|
86
88
|
connection losses in between message send and receive; lost messages
|
87
89
|
will be retransmitted. Some Protocol classes provide support for replay
|
88
|
-
protection through in-band
|
89
|
-
If a
|
90
|
+
protection through in-band UUIDs; though it is not a requirement to implement it.
|
91
|
+
If a UUID is provided in the data stream, the Protocol will not call
|
90
92
|
the handler again for retransmissions, but instead reply with the old,
|
91
93
|
already evaluated value.
|
92
94
|
|
93
|
-
Not all protocols support
|
95
|
+
Not all protocols support UUIDs; those who do not offer no replay protection,
|
94
96
|
and special care has to be taken elsewhere.
|
95
97
|
|
98
|
+
All object-encoding protocols support UUIDs, including YAML and Marshal.
|
99
|
+
XMLRPC does not.
|
100
|
+
|
96
101
|
== Benchmarks
|
97
102
|
|
98
103
|
There is a benchmark script included in the git repository (and in the gem
|
@@ -101,11 +106,13 @@ under tools/). A sample output follows; your milage may vary.
|
|
101
106
|
user system total real
|
102
107
|
|
103
108
|
native DRb
|
104
|
-
1 0.000000 0.000000 0.000000 ( 0.
|
105
|
-
1000 0.
|
109
|
+
1 0.000000 0.000000 0.000000 ( 0.000172)
|
110
|
+
1000 0.110000 0.010000 0.120000 ( 0.119767)
|
106
111
|
|
107
|
-
|
112
|
+
Arpie: proxied MarshalProtocol with replay protection through uuidtools
|
113
|
+
1 0.000000 0.000000 0.010000 ( 0.075373)
|
114
|
+
1000 0.530000 0.090000 0.600000 ( 0.608665)
|
108
115
|
|
109
|
-
Arpie: proxied MarshalProtocol
|
110
|
-
1 0.000000 0.000000 0.000000 ( 0.
|
111
|
-
1000 0.
|
116
|
+
Arpie: proxied MarshalProtocol without replay protection
|
117
|
+
1 0.000000 0.000000 0.000000 ( 0.000173)
|
118
|
+
1000 0.170000 0.020000 0.190000 ( 0.194649)
|
data/Rakefile
CHANGED
@@ -9,7 +9,7 @@ include FileUtils
|
|
9
9
|
# Configuration
|
10
10
|
##############################################################################
|
11
11
|
NAME = "arpie"
|
12
|
-
VERS = "0.0.
|
12
|
+
VERS = "0.0.3"
|
13
13
|
CLEAN.include ["**/.*.sw?", "pkg", ".config", "rdoc", "coverage"]
|
14
14
|
RDOC_OPTS = ["--quiet", "--line-numbers", "--inline-source", '--title', \
|
15
15
|
"#{NAME}: A high-performing layered networking protocol framework. Simple to use, simple to extend.", \
|
@@ -44,6 +44,7 @@ spec = Gem::Specification.new do |s|
|
|
44
44
|
s.files = %w(COPYING README Rakefile) + Dir.glob("{bin,doc,spec,lib,tools,scripts,data}/**/*")
|
45
45
|
s.require_path = "lib"
|
46
46
|
s.bindir = "bin"
|
47
|
+
s.add_dependency('uuidtools', '>= 1.0.7')
|
47
48
|
end
|
48
49
|
|
49
50
|
Rake::GemPackageTask.new(spec) do |p|
|
data/lib/arpie/client.rb
CHANGED
data/lib/arpie/protocol.rb
CHANGED
data/lib/arpie/proxy.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'uuidtools'
|
2
|
+
|
1
3
|
module Arpie
|
2
4
|
|
3
5
|
# A Endpoint which supports arbitary objects as handlers,
|
@@ -6,30 +8,61 @@ module Arpie
|
|
6
8
|
# Note that this will only export public instance method
|
7
9
|
# of the class as they are defined.
|
8
10
|
class ProxyServer < Server
|
11
|
+
|
12
|
+
# An array containing symbols of method names that the
|
13
|
+
# handler should be allowed to call. Defaults to
|
14
|
+
# all public instance methods the class defines (wysiwyg).
|
15
|
+
# Set this to nil to allow calling of ALL methods, but be
|
16
|
+
# warned of the security implications (instance_eval, ..).
|
9
17
|
attr_accessor :interface
|
10
18
|
|
11
|
-
# Set
|
12
|
-
|
13
|
-
|
14
|
-
#
|
15
|
-
#
|
16
|
-
|
17
|
-
|
18
|
-
def
|
19
|
+
# Set this to false to disable replay protection.
|
20
|
+
attr_accessor :uuid_tracking
|
21
|
+
|
22
|
+
# The maximum number of method call results to remember.
|
23
|
+
# Defaults to 100, which should be enough for everyone. ;)
|
24
|
+
attr_accessor :max_uuids
|
25
|
+
|
26
|
+
def initialize *va
|
27
|
+
super
|
28
|
+
@uuids = {}
|
29
|
+
@max_uuids = 100
|
30
|
+
@uuid_tracking = true
|
31
|
+
end
|
32
|
+
|
33
|
+
# Set a class handler. All public instance methods will be
|
34
|
+
# callable over RPC (with a Proxy object) (see attribute interface).
|
35
|
+
def handle handler
|
19
36
|
@handler = handler
|
20
|
-
@interface =
|
37
|
+
@interface = handler.class.public_instance_methods(false).map {|x|
|
38
|
+
x.to_sym
|
39
|
+
}
|
21
40
|
self
|
22
41
|
end
|
23
42
|
|
24
43
|
private
|
25
44
|
|
26
45
|
def _handle endpoint, message
|
27
|
-
if !@handler.respond_to?(message.meth) ||
|
46
|
+
if !@handler.respond_to?(message.meth.to_sym) ||
|
47
|
+
(@interface && !@interface.index(message.meth.to_sym))
|
28
48
|
raise NoMethodError, "No such method: #{message.meth.inspect}"
|
29
49
|
end
|
30
50
|
|
31
|
-
|
32
|
-
|
51
|
+
# Prune old serials. This can probably be optimized, but works well enough for now.
|
52
|
+
if @uuid_tracking && message.uuid
|
53
|
+
timestamps = @uuids.values.map {|v| v[0] }.sort
|
54
|
+
latest_timestamp = timestamps[-@max_uuids]
|
55
|
+
@uuids.reject! {|uuid, (time, value)|
|
56
|
+
time < latest_timestamp
|
57
|
+
} if latest_timestamp
|
58
|
+
|
59
|
+
endpoint.write_message((@uuids[message.uuid] ||=
|
60
|
+
[Time.now, @handler.send(message.meth, *message.argv)])[1])
|
61
|
+
|
62
|
+
else
|
63
|
+
endpoint.write_message @handler.send(message.meth, *message.argv)
|
64
|
+
end
|
65
|
+
|
33
66
|
end
|
34
67
|
end
|
35
68
|
|
@@ -39,11 +72,30 @@ module Arpie
|
|
39
72
|
class ProxyClient < RPCClient
|
40
73
|
|
41
74
|
def initialize protocol, namespace = ""
|
75
|
+
super(protocol)
|
42
76
|
@protocol, @namespace = protocol, namespace
|
77
|
+
@uuid_generator = lambda {|client, method, argv|
|
78
|
+
UUID.timestamp_create.to_i.to_s
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
# Set up a new UUID generator for this proxy client.
|
83
|
+
# Make sure that this yields really random numbers.
|
84
|
+
# The default uses the uuidtools gem and is usually okay.
|
85
|
+
# You can simply set this to nil (by calling it without
|
86
|
+
# a block).
|
87
|
+
#
|
88
|
+
# Note that disabling this disables replay protection.
|
89
|
+
def uuid_generator &handler #:yields: client, method, argv
|
90
|
+
@uuid_generator = handler
|
91
|
+
self
|
43
92
|
end
|
44
93
|
|
45
94
|
def method_missing meth, *argv # :nodoc:
|
46
|
-
|
95
|
+
uuid = @uuid_generator ?
|
96
|
+
@uuid_generator.call(self, meth, argv) : nil
|
97
|
+
|
98
|
+
call = RPCProtocol::Call.new(@namespace, meth, argv, uuid)
|
47
99
|
ret = self.request(call)
|
48
100
|
case ret
|
49
101
|
when Exception
|
data/lib/arpie/server.rb
CHANGED
@@ -7,18 +7,19 @@ module Arpie
|
|
7
7
|
class Endpoint
|
8
8
|
attr_reader :io
|
9
9
|
|
10
|
-
attr_reader :
|
10
|
+
attr_reader :protocol
|
11
11
|
|
12
|
-
def initialize
|
13
|
-
@
|
12
|
+
def initialize protocol, io
|
13
|
+
@protocol, @io = protocol, io
|
14
|
+
@protocol.reset
|
14
15
|
end
|
15
16
|
|
16
17
|
def read_message
|
17
|
-
@
|
18
|
+
@protocol.read_message(@io)
|
18
19
|
end
|
19
20
|
|
20
21
|
def write_message message
|
21
|
-
@
|
22
|
+
@protocol.write_message(@io, message)
|
22
23
|
end
|
23
24
|
alias_method :<<, :write_message
|
24
25
|
|
@@ -119,7 +120,7 @@ module Arpie
|
|
119
120
|
def _acceptor_thread
|
120
121
|
loop do
|
121
122
|
client = @acceptor.call(self)
|
122
|
-
c = @protocol.endpoint_klass.new(
|
123
|
+
c = @protocol.endpoint_klass.new(@protocol.clone, client)
|
123
124
|
Thread.new { _read_thread(c) }
|
124
125
|
end
|
125
126
|
end
|
data/tools/benchmark.rb
CHANGED
@@ -39,27 +39,13 @@ Benchmark.bm {|b|
|
|
39
39
|
b.report("1000") { 1000.times { drbobject.reverse "benchmark" } }
|
40
40
|
|
41
41
|
puts ""
|
42
|
-
puts "
|
43
|
-
#server = XMLRPC::Server.new(51211, "127.0.0.1", 4, nil, false)
|
44
|
-
#server.add_handler(XMLRPC::iPIMethods("wrap"), Wrap.new)
|
45
|
-
#server_thread = Thread.new { server.serve }
|
46
|
-
#client = XMLRPC::Client.new( "127.0.0.1", "/", 51211)
|
47
|
-
#b.report(" 1") { 1.times { client.call("wrap.reverse", "benchmark") } }
|
48
|
-
#b.report("1000") { 1000.times { client.call("wrap.reverse", "benchmark") } }
|
49
|
-
#server.shutdown
|
50
|
-
#server_thread.join
|
51
|
-
|
52
|
-
puts ""
|
53
|
-
puts "Arpie: proxied MarshalProtocol"
|
42
|
+
puts "Arpie: proxied MarshalProtocol with replay protection through uuidtools"
|
54
43
|
b.report(" 1") { 1.times { $proxy.reverse "benchmark" } }
|
55
44
|
b.report("1000") { 1000.times { $proxy.reverse "benchmark" } }
|
56
45
|
|
57
|
-
|
58
|
-
def evented_call
|
59
|
-
$transport.request(ProxyCall.new("reverse",["benchmark"])) do end
|
60
|
-
end
|
61
46
|
puts ""
|
62
|
-
|
63
|
-
|
64
|
-
|
47
|
+
puts "Arpie: proxied MarshalProtocol without replay protection"
|
48
|
+
$proxy.uuid_generator
|
49
|
+
b.report(" 1") { 1.times { $proxy.reverse "benchmark" } }
|
50
|
+
b.report("1000") { 1000.times { $proxy.reverse "benchmark" } }
|
65
51
|
}
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: arpie
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bernhard Stoeckner
|
@@ -9,10 +9,19 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-02-
|
12
|
+
date: 2009-02-11 00:00:00 +01:00
|
13
13
|
default_executable:
|
14
|
-
dependencies:
|
15
|
-
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: uuidtools
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.0.7
|
24
|
+
version:
|
16
25
|
description: A high-performing layered networking protocol framework. Simple to use, simple to extend.
|
17
26
|
email: elven@swordcoast.net
|
18
27
|
executables: []
|