arpie 0.0.2 → 0.0.3
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/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: []
|