arpie 0.0.6 → 0.1.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.
@@ -1,157 +0,0 @@
1
- module Arpie
2
-
3
- # Endpoint wraps client IO objects. One Endpoint
4
- # per client. This is provided as a convenience
5
- # mechanism for protocols to store
6
- # protocol-and-client-specific data.
7
- class Endpoint
8
- attr_reader :io
9
-
10
- attr_reader :protocol
11
-
12
- def initialize protocol, io
13
- @protocol, @io = protocol, io
14
- @protocol.reset
15
- end
16
-
17
- def read_message
18
- @protocol.read_message(@io)
19
- end
20
-
21
- def write_message message
22
- @protocol.write_message(@io, message)
23
- end
24
- alias_method :<<, :write_message
25
-
26
- end
27
-
28
- # A Server is the server-side part of a RPC setup.
29
- # It accepts connections (via the acceptor), and handles
30
- # incoming RPC calls on them.
31
- #
32
- # There will be one Thread per connection, so order of
33
- # execution with multiple threads is not guaranteed.
34
- class Server
35
- attr_reader :protocol
36
-
37
- attr_reader :endpoints
38
-
39
- # Create a new Server with the given protocols.
40
- # You will need to define a handler, and an acceptor
41
- # before it becomes operational.
42
- def initialize *protocols
43
- @protocol = Arpie::ProtocolChain.new(*protocols)
44
- @endpoints = []
45
-
46
- @on_connect = lambda {|server, endpoint| }
47
- @on_disconnect = lambda {|server, endpoint, exception| }
48
- @on_handler_error = lambda {|server, endpoint, message, exception|
49
- $stderr.puts "Error in handler: #{exception.message.to_s}"
50
- $stderr.puts exception.backtrace.join("\n")
51
- $stderr.puts "Returning exception for this call."
52
- Exception.new("internal error")
53
- }
54
- @handler = lambda {|server, endpoint, message| raise ArgumentError, "No handler defined." }
55
- end
56
-
57
- # Provide an acceptor; this will be run in a a loop
58
- # to get IO objects.
59
- #
60
- # Example:
61
- # listener = TCPServer.new(12345)
62
- # my_server.accept do
63
- # listener.accept
64
- # end
65
- def accept &acceptor #:yields: server
66
- @acceptor = acceptor
67
- Thread.new { _acceptor_thread }
68
- self
69
- end
70
-
71
- # Set a message handler, which is a proc that will receive
72
- # three parameters: the server, the endpoint, and the message.
73
- #
74
- # Example:
75
- # my_server.handle do |server, endpoint, message|
76
- # puts "Got a message: #{message.inspect}"
77
- # endpoint.write_message "ok"
78
- # end
79
- def handle &handler #:yields: server, endpoint, message
80
- raise ArgumentError, "No handler given; need a block or proc." unless handler
81
- @handler = handler
82
- self
83
- end
84
-
85
- # Set an error handler.
86
- # The return value will be sent to the client.
87
- #
88
- # Default is to print the exception to stderr, and return
89
- # a generic exception that does not leak information.
90
- def on_handler_error &handler #:yields: server, endpoint, message, exception
91
- raise ArgumentError, "No handler given; need a block or proc." unless handler
92
- @on_handler_error = handler
93
- self
94
- end
95
-
96
- # Callback that gets invoked when a new client connects.
97
- # You can <tt>throw :kill_client</tt> here to stop this client
98
- # from connecting. Clients stopped this way will invoke
99
- # the on_disconnect handler normally.
100
- def on_connect &handler #:yields: server, endpoint
101
- raise ArgumentError, "No handler given; need a block or proc." unless handler
102
- @on_connect = handler
103
- self
104
- end
105
-
106
- # Callback that gets invoked when a client disconnects.
107
- # The exception is the error that occured (usually a EOFError).
108
- def on_disconnect &handler #:yields: server, endpoint, exception
109
- raise ArgumentError, "No handler given; need a block or proc." unless handler
110
- @on_disconnect = handler
111
- self
112
- end
113
-
114
- private
115
-
116
- def _handle endpoint, message
117
- @handler.call(self, endpoint, message)
118
- end
119
-
120
- def _acceptor_thread
121
- loop do
122
- client = @acceptor.call(self)
123
- c = @protocol.endpoint_class.new(@protocol.clone, client)
124
- Thread.new { _read_thread(c) }
125
- end
126
- end
127
-
128
- def _read_thread endpoint
129
- @endpoints << endpoint
130
- _exception = nil
131
-
132
- catch(:kill_client) {
133
- @on_connect.call(self, endpoint)
134
-
135
- loop do
136
- message, answer = nil, nil
137
-
138
- begin
139
- message = endpoint.read_message
140
- rescue IOError => e
141
- _exception = e
142
- break
143
- end
144
-
145
- begin
146
- answer = _handle(endpoint, message)
147
- rescue Exception => e
148
- answer = @on_handler_error.call(self, endpoint, message, e)
149
- end
150
- end
151
- }
152
-
153
- @on_disconnect.call(self, endpoint, _exception)
154
- @endpoints.delete(endpoint)
155
- end
156
- end
157
- end
@@ -1,108 +0,0 @@
1
- require 'xmlrpc/create'
2
- require 'xmlrpc/parser'
3
- require 'xmlrpc/config'
4
-
5
- module Arpie
6
-
7
- class XMLRPCProtocol < Protocol
8
- private_class_method :new
9
-
10
- attr_accessor :writer
11
- attr_accessor :parser
12
-
13
- def setup
14
- @writer ||= XMLRPC::Create.new
15
- @parser ||= XMLRPC::XMLParser::REXMLStreamParser.new
16
- end
17
- private :setup
18
-
19
- end
20
-
21
- # A XMLRPC Protocol based on rubys xmlrpc stdlib.
22
- # This does not encode HTTP headers; usage together with
23
- # a real webserver is advised.
24
- class XMLRPCClientProtocol < XMLRPCProtocol
25
- public_class_method :new
26
-
27
- def to object
28
- setup
29
- raise ArgumentError, "Can only encode Arpie::RPCall" unless
30
- object.is_a?(Arpie::RPCall)
31
-
32
- yield @writer.methodCall((object.ns.nil? ? '' : object.ns + '.') + object.meth, *object.argv)
33
- end
34
-
35
- def from binary
36
- setup
37
- yield @parser.parseMethodResponse(binary)[1]
38
- end
39
- end
40
-
41
- class XMLRPCServerProtocol < XMLRPCProtocol
42
- public_class_method :new
43
-
44
- def to object
45
- setup
46
- yield case object
47
- when Exception
48
- # TODO: wrap XMLFault
49
- raise NotImplementedError, "Cannot encode exceptions"
50
-
51
- else
52
- @writer.methodResponse(true, object)
53
- end
54
- end
55
-
56
- def from binary
57
- setup
58
- vv = @parser.parseMethodCall(binary)
59
- ns, meth = vv[0].split('.')
60
- meth.nil? and begin meth, ns = ns, nil end
61
- yield RPCall.new(ns, meth, vv[1])
62
- end
63
- end
64
-
65
- # This simulates a very basic HTTP XMLRPC client/server.
66
- # It is not recommended to use this with production code.
67
- class HTTPTestProtocol < Protocol
68
- CAN_SEPARATE_MESSAGES = true
69
-
70
- private_class_method :new
71
-
72
- def from binary
73
- # Simply strip all HTTP headers.
74
- binary && binary.size > 0 or incomplete!
75
- header, body_and_rest = binary.split(/\r\n\r\n/, 2)
76
- header && body_and_rest or incomplete!
77
-
78
- header =~ /^\s*content-length:\s+(\d+)$\s*/xi or stream_error! "No content-length was provided."
79
- content_length = $1.to_i
80
-
81
- content_length <= 0
82
- body_and_rest.size >= content_length or incomplete!
83
-
84
- body = body_and_rest[0, content_length]
85
-
86
- yield body
87
-
88
- header.size + 4 + content_length
89
- end
90
- end
91
-
92
- class HTTPClientTestProtocol < HTTPTestProtocol
93
- public_class_method :new
94
-
95
- def to object
96
- yield "GET / HTTP/1.[01]\r\nContent-Length: #{object.size}\r\n\r\n" + object
97
- end
98
-
99
- end
100
-
101
- class HTTPServerTestProtocol < HTTPTestProtocol
102
- public_class_method :new
103
-
104
- def to object
105
- yield "HTTP/1.0 200 OK\r\nContent-Length: #{object.size}\r\n\r\n" + object
106
- end
107
- end
108
- end
@@ -1,107 +0,0 @@
1
- require File.join(File.dirname(__FILE__), 'spec_helper')
2
-
3
- require 'socket'
4
- require 'timeout'
5
-
6
- describe "ClientServer", :shared => true do
7
- before(:all) do
8
- @tcp_server = TCPServer.new('127.0.0.1', 56000)
9
- end
10
-
11
- before do
12
- @handler_errors = 0
13
- $evaluate_calls = 0
14
- @client = Arpie::Client.new(Arpie::MarshalProtocol.new, Arpie::SizedProtocol.new)
15
- @server = Arpie::Server.new(Arpie::MarshalProtocol.new, Arpie::SizedProtocol.new)
16
- @client.connect do TCPSocket.new('127.0.0.1', 56000) end
17
- @server.accept do @tcp_server.accept end
18
- @server.on_handler_error do |s, e, p, x|
19
- @handler_errors += 1
20
- end
21
-
22
- @server.handle do |s, e, m|
23
- e.write_message m
24
- end
25
- end
26
- end
27
-
28
- describe "ProxyClientServer", :shared => true do
29
- before(:all) do
30
- @tcp_server = TCPServer.new('127.0.0.1', 56001)
31
- end
32
-
33
- before do
34
- @handler_errors = 0
35
- $evaluate_calls = 0
36
- @client = Arpie::ProxyClient.new(Arpie::MarshalProtocol.new, Arpie::SizedProtocol.new)
37
- @server = Arpie::ProxyServer.new(Arpie::MarshalProtocol.new, Arpie::SizedProtocol.new)
38
- @client.connect do TCPSocket.new('127.0.0.1', 56001) end
39
- @server.accept do @tcp_server.accept end
40
- @server.on_handler_error do |s, e, p, x|
41
- @handler_errors += 1
42
- end
43
-
44
- @server.handle((Class.new do
45
- def evaluate_calls
46
- $evaluate_calls += 1
47
- end
48
- def raise_something
49
- raise "test"
50
- end
51
- end).new)
52
- end
53
- end
54
-
55
- describe Arpie::Client do
56
- it_should_behave_like "ClientServer"
57
- end
58
-
59
- describe Arpie::EventedClient do
60
- it_should_behave_like "ClientServer"
61
-
62
- before do
63
- @client = Arpie::EventedClient.new(Arpie::MarshalProtocol.new, Arpie::SizedProtocol.new)
64
- @handler_calls = 0
65
- @queue = Queue.new
66
- @client.handle do
67
- @queue.push true
68
- @handler_calls += 1
69
- end
70
- @client.connect do TCPSocket.new('127.0.0.1', 56000) end
71
- end
72
-
73
- it "should not allow reading messages" do
74
- @client.should_not respond_to :read_message
75
- end
76
-
77
- it "should call the handler for incoming messages" do
78
- @client.write_message "test"
79
- lambda {Timeout.timeout(1.0) { @queue.pop } }.should_not raise_error TimeoutError
80
- end
81
- end
82
-
83
- describe Arpie::Server do
84
- it_should_behave_like "ClientServer"
85
- end
86
-
87
- describe "ProxyServer" do
88
- it_should_behave_like "ProxyClientServer"
89
- it "should raise handler errors to the client" do
90
- lambda { @client.raise_something }.should(raise_error RuntimeError, "Internal Error")
91
- @handler_errors.should == 1
92
- end
93
-
94
- it "should not re-evaluate for already-seen uuids" do
95
- @client.evaluate_calls
96
- @client.evaluate_calls
97
- @client.serial -= 1
98
- @client.evaluate_calls
99
- @client.evaluate_calls
100
- $evaluate_calls.should == 3
101
- end
102
-
103
- it "should not call handler errors for missing methods" do
104
- lambda { @client.missing }.should raise_error NoMethodError
105
- $evaluate_calls.should == 0
106
- end
107
- end
@@ -1,52 +0,0 @@
1
- require 'rubygems'
2
- require 'socket'
3
- $:.unshift(File.join(File.dirname(__FILE__), "../lib/"))
4
- require 'arpie'
5
- require 'benchmark'
6
- require 'drb'
7
- require 'xmlrpc/server'
8
- require 'xmlrpc/client'
9
-
10
- class Wrap
11
- def reverse x
12
- x.reverse
13
- end
14
- end
15
-
16
- include Arpie
17
-
18
- server = TCPServer.new(51210)
19
-
20
- endpoint = ProxyServer.new MarshalProtocol.new, SizedProtocol.new
21
- endpoint.handle Wrap.new
22
-
23
- endpoint.accept do
24
- server.accept
25
- end
26
-
27
- $proxy = ProxyClient.new MarshalProtocol.new, SizedProtocol.new
28
- $proxy.connect(true) do
29
- TCPSocket.new("127.0.0.1", 51210)
30
- end
31
-
32
- Benchmark.bm {|b|
33
-
34
- puts ""
35
- puts "native DRb"
36
- drbserver = DRb.start_service nil, Wrap.new
37
- drbobject = DRbObject.new nil, DRb.uri
38
-
39
- b.report(" 1") { 1.times { drbobject.reverse "benchmark" } }
40
- b.report("1000") { 1000.times { drbobject.reverse "benchmark" } }
41
-
42
- puts ""
43
- puts "Arpie: proxied MarshalProtocol with replay protection through uuidtools"
44
- b.report(" 1") { 1.times { $proxy.reverse "benchmark" } }
45
- b.report("1000") { 1000.times { $proxy.reverse "benchmark" } }
46
-
47
- puts ""
48
- puts "Arpie: proxied MarshalProtocol without replay protection"
49
- $proxy.replay_protection = false
50
- b.report(" 1") { 1.times { $proxy.reverse "benchmark" } }
51
- b.report("1000") { 1000.times { $proxy.reverse "benchmark" } }
52
- }
@@ -1,42 +0,0 @@
1
- require 'rubygems'
2
- $:.unshift(File.join(File.dirname(__FILE__), "../lib/"))
3
- require 'socket'
4
- require 'arpie'
5
- require 'benchmark'
6
-
7
- include Arpie
8
-
9
- # Data test size.
10
- DATA_SIZE = 512
11
-
12
- rpc_call = RPCall.new('ns.', 'meth', [1, 2, 3, 4])
13
- $test_data = "a" * DATA_SIZE
14
- $test_data.freeze
15
-
16
- # Protocols to test:
17
- PROTOCOLS = [
18
- [SizedProtocol.new],
19
- [MarshalProtocol.new, SizedProtocol.new],
20
- [YAMLProtocol.new]
21
- ]
22
-
23
- ITERATIONS = 1000
24
-
25
- $stderr.puts "Testing protocols with a data size of #{DATA_SIZE}, #{ITERATIONS} iterations"
26
-
27
-
28
- Benchmark.bm {|b|
29
- r, w = IO.pipe
30
- PROTOCOLS.each {|p|
31
- a ||= []
32
- proto = ProtocolChain.new *p
33
- r, w = IO.pipe
34
-
35
- b.report("%-30s\n" % p.map{|x| x.class.to_s}.inspect) {
36
- ITERATIONS.times do
37
- proto.write_message(w, $test_data)
38
- proto.read_message(r)
39
- end
40
- }
41
- }
42
- }