dripdrop 0.10.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/.document +5 -0
  2. data/.gitignore +31 -0
  3. data/Gemfile +5 -0
  4. data/LICENSE +20 -0
  5. data/README.md +164 -0
  6. data/Rakefile +16 -0
  7. data/dripdrop.gemspec +37 -0
  8. data/example/agent_test.rb +14 -0
  9. data/example/combined.rb +33 -0
  10. data/example/complex/README +22 -0
  11. data/example/complex/client.rb +20 -0
  12. data/example/complex/server.rb +102 -0
  13. data/example/complex/service.rb +8 -0
  14. data/example/complex/websocket.rb +442 -0
  15. data/example/http.rb +23 -0
  16. data/example/pubsub.rb +29 -0
  17. data/example/pushpull.rb +21 -0
  18. data/example/subclass.rb +54 -0
  19. data/example/xreq_xrep.rb +24 -0
  20. data/js/dripdrop.html +186 -0
  21. data/js/dripdrop.js +107 -0
  22. data/js/qunit.css +155 -0
  23. data/js/qunit.js +1261 -0
  24. data/lib/dripdrop.rb +2 -0
  25. data/lib/dripdrop/agent.rb +40 -0
  26. data/lib/dripdrop/handlers/base.rb +42 -0
  27. data/lib/dripdrop/handlers/http_client.rb +38 -0
  28. data/lib/dripdrop/handlers/http_server.rb +59 -0
  29. data/lib/dripdrop/handlers/mongrel2.rb +163 -0
  30. data/lib/dripdrop/handlers/websocket_server.rb +86 -0
  31. data/lib/dripdrop/handlers/zeromq.rb +300 -0
  32. data/lib/dripdrop/message.rb +190 -0
  33. data/lib/dripdrop/node.rb +351 -0
  34. data/lib/dripdrop/node/nodelet.rb +35 -0
  35. data/lib/dripdrop/version.rb +3 -0
  36. data/spec/gimite-websocket.rb +442 -0
  37. data/spec/message_spec.rb +94 -0
  38. data/spec/node/http_spec.rb +77 -0
  39. data/spec/node/nodelet_spec.rb +67 -0
  40. data/spec/node/routing_spec.rb +67 -0
  41. data/spec/node/websocket_spec.rb +98 -0
  42. data/spec/node/zmq_m2_spec.rb +77 -0
  43. data/spec/node/zmq_pushpull_spec.rb +54 -0
  44. data/spec/node/zmq_xrepxreq_spec.rb +108 -0
  45. data/spec/node_spec.rb +85 -0
  46. data/spec/spec_helper.rb +20 -0
  47. metadata +167 -0
@@ -0,0 +1,94 @@
1
+ require 'spec_helper'
2
+
3
+ class SpecMessageClass < DripDrop::Message
4
+ include DripDrop::SubclassedMessage
5
+ end
6
+
7
+ describe DripDrop::Message do
8
+ describe "basic message" do
9
+ def create_basic
10
+ attrs = {
11
+ :name => 'test',
12
+ :head => {'foo' => 'bar'},
13
+ :body => ['foo', 'bar', 'baz']
14
+ }
15
+ message = DripDrop::Message.new(attrs[:name],:head => attrs[:head],
16
+ :body => attrs[:body])
17
+ [message, attrs]
18
+ end
19
+ it "should create a basic message without raising an exception" do
20
+ lambda {
21
+ message, attrs = create_basic
22
+ }.should_not raise_exception
23
+ end
24
+ describe "with minimal attributes" do
25
+ it "should create a message with only a name" do
26
+ lambda {
27
+ DripDrop::Message.new('nameonly')
28
+ }.should_not raise_exception
29
+ end
30
+ it "should set the head to a single key hash containing message class if nil provided" do
31
+ DripDrop::Message.new('nilhead', :head => nil).head.should == {'message_class' => 'DripDrop::Message'}
32
+ end
33
+ it "should raise an exception if a non-hash, non-nil head is provided" do
34
+ lambda {
35
+ DripDrop::Message.new('arrhead', :head => [])
36
+ }.should raise_exception(ArgumentError)
37
+ end
38
+ end
39
+ describe "encoding" do
40
+ before(:all) do
41
+ @message, @attrs = create_basic
42
+ end
43
+ it "should encode to valid JSON hash without error" do
44
+ enc = @message.encoded
45
+ enc.should be_a(String)
46
+ JSON.parse(enc).should be_a(Hash)
47
+ end
48
+ it "should decode encoded messages without errors" do
49
+ DripDrop::Message.decode(@message.encoded).should be_a(DripDrop::Message)
50
+ end
51
+ it "should decode JSON encoded messages without errors" do
52
+ DripDrop::Message.decode_json(@message.json_encoded).should be_a(DripDrop::Message)
53
+ end
54
+ it "should convert messages to Hash representations" do
55
+ @message.to_hash.should be_a(Hash)
56
+ end
57
+ it "should be able to turn hash representations back into Message objs" do
58
+ DripDrop::Message.from_hash(@message.to_hash).should be_a(DripDrop::Message)
59
+ end
60
+ end
61
+ describe "subclassing" do
62
+ def create_auto_message
63
+ attrs = {
64
+ :name => 'test',
65
+ :head => {'foo' => 'bar', 'message_class' => 'SpecMessageClass'},
66
+ :body => ['foo', 'bar', 'baz']
67
+ }
68
+
69
+ message = DripDrop::AutoMessageClass.create_message(attrs)
70
+
71
+ [message, attrs]
72
+ end
73
+ before(:all) do
74
+ @message, @attrs = create_auto_message
75
+ end
76
+ it "should be added to the subclass message class hash if SubclassedMessage included" do
77
+ DripDrop::AutoMessageClass.message_subclasses.should include('SpecMessageClass' => SpecMessageClass)
78
+ end
79
+ it "should throw an exception if we try to recreate a message of the wrong class" do
80
+ msg = DripDrop::Message.new('test')
81
+ lambda{SpecMessageClass.recreate_message(msg.to_hash)}.should raise_exception
82
+ end
83
+
84
+ describe "DripDrop::AutoMessageClass" do
85
+ it "should create a properly classed message based on head['message_class']" do
86
+ @message.should be_a(SpecMessageClass)
87
+ end
88
+ it "should recreate a message based on head['message_class']" do
89
+ DripDrop::AutoMessageClass.recreate_message(@message.to_hash).should be_a(SpecMessageClass)
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,77 @@
1
+ require 'spec_helper'
2
+
3
+ require 'set'
4
+
5
+ if defined?(EM::HttpServer)
6
+ describe "http" do
7
+
8
+ def http_send_messages(to_send,addr=rand_addr('http'),&block)
9
+ responses = []
10
+ client = nil
11
+ server = nil
12
+
13
+ @node = run_reactor(2) do
14
+ zmq_subscribe(rand_addr, :bind) do |message|
15
+ end
16
+
17
+ client = http_client(addr)
18
+
19
+ server = http_server(addr).on_recv do |message,response|
20
+ $stdout.flush
21
+ responses << message
22
+ response.send_message(message)
23
+ end
24
+
25
+ to_send.each do |message|
26
+ EM::next_tick do
27
+ http_client(addr).send_message(message) do |resp_message|
28
+ block.call(message,resp_message)
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ {:responses => responses, :handlers => {:server => [server] }}
35
+ end
36
+
37
+ shared_examples_for "all http nodes" do
38
+ describe "basic sending and receiving" do
39
+ before(:all) do
40
+ @sent = []
41
+ 10.times {|i| @sent << DripDrop::Message.new("test-#{i}")}
42
+ @client_responses = []
43
+ @http_info = http_send_messages(@sent,@http_test_addr) do |sent_message,resp_message|
44
+ @client_responses << {:sent_message => sent_message,
45
+ :resp_message => resp_message}
46
+ end
47
+ @responses = @http_info[:responses]
48
+ end
49
+
50
+ it "should receive all sent messages" do
51
+ resp_names = @responses.map(&:name).inject(Set.new) {|memo,rn| memo << rn}
52
+ @sent.map(&:name).each {|sn| resp_names.should include(sn)}
53
+ end
54
+
55
+ it "should return to the client as many responses as sent messages" do
56
+ @client_responses.length.should == @sent.length
57
+ end
58
+
59
+ it "should return to the client an identical message to that which was sent" do
60
+ @client_responses.each do |resp|
61
+ resp[:sent_message].name.should == resp[:resp_message].name
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ describe "http apps using the URL root (/)" do
68
+ before(:all) { @http_test_addr = rand_addr('http') }
69
+ it_should_behave_like "all http nodes"
70
+ end
71
+
72
+ describe "http apps using a subdirectory of the URL (/subdir)" do
73
+ before(:all) { @http_test_addr = rand_addr('http') + '/subdir' }
74
+ it_should_behave_like "all http nodes"
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+
3
+ describe "nodelets" do
4
+ class SpecialNodelet < DripDrop::Node::Nodelet
5
+ def action
6
+ route :worker1, :zmq_pull, distributor_output.address, :connect
7
+ route :worker2, :zmq_pull, distributor_output.address, :connect
8
+ end
9
+ end
10
+
11
+ before(:all) do
12
+ nodelets = {}
13
+
14
+
15
+ @node = run_reactor do
16
+ nodelet :distributor do |nlet|
17
+ nlet.route :output, :zmq_push, rand_addr, :bind
18
+ end
19
+
20
+ nodelet :worker_cluster, SpecialNodelet
21
+ end
22
+
23
+ @nodelets = @node.nodelets
24
+ end
25
+
26
+ it "should create the nodelets" do
27
+ @nodelets.length.should == 2
28
+ end
29
+
30
+ it "should pass a DripDrop::Node::Nodelet to the block" do
31
+ @nodelets.values.each do |nlet|
32
+ nlet.should be_kind_of(DripDrop::Node::Nodelet)
33
+ end
34
+ end
35
+
36
+ it "should give access to the full routing table to nodelets" do
37
+ @node.routing.each do |route_name,handler|
38
+ @nodelets.values.each do |nlet|
39
+ nlet.send(route_name).should == handler
40
+ end
41
+ end
42
+ end
43
+
44
+ it "should use the class SpecialNodelet for the nodelet assigned that" do
45
+ @nodelets[:worker_cluster].should be_a(SpecialNodelet)
46
+ end
47
+
48
+ it "should return a DripDrop::Handler for short routes" do
49
+ @nodelets[:distributor].send(:output).should be_a(DripDrop::BaseHandler)
50
+ end
51
+
52
+ it "should return a DripDrop::Handler for long routes" do
53
+ @nodelets[:distributor].send(:distributor_output).should be_a(DripDrop::BaseHandler)
54
+ end
55
+
56
+ it "should define prefix-less versions of nodelet specific routes" do
57
+ {
58
+ @nodelets[:worker_cluster] => {:worker1 => :worker_cluster_worker1,
59
+ :worker2 => :worker_cluster_worker2},
60
+ @nodelets[:distributor] => {:output => :distributor_output}
61
+ }.each do |nlet, mapping|
62
+ mapping.each do |short,long|
63
+ nlet.send(short).should == nlet.send(long)
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+
3
+ describe "routing" do
4
+ shared_examples_for "all routing" do
5
+ it "should define all routes in the table" do
6
+ @expected_routing.keys.each do |route_name|
7
+ @node.routing.keys.should include(route_name)
8
+ end
9
+ end
10
+
11
+ it "should define a handler in the routing table for each route" do
12
+ @expected_routing.keys.each do |route_name|
13
+ @node.routing[route_name].should be_kind_of(DripDrop::BaseHandler)
14
+ end
15
+ end
16
+
17
+ it "should define a singleton method for each entry in the routing table" do
18
+ @expected_routing.keys.each do |route_name|
19
+ @node.send(route_name).should == @node.routing[route_name]
20
+ end
21
+ end
22
+
23
+ it "should create handlers with the correct properties" do
24
+ @expected_routing.each do |route_name,expected_props|
25
+ handler = @node.send(route_name)
26
+ handler.class.should == expected_props[:class]
27
+ end
28
+ end
29
+ end
30
+
31
+ context "with no groups" do
32
+ before(:all) do
33
+ @expected_routing = {
34
+ :distributor => {:class => DripDrop::ZMQPushHandler},
35
+ :worker1 => {:class => DripDrop::ZMQPullHandler},
36
+ :worker2 => {:class => DripDrop::ZMQPullHandler}
37
+ }
38
+ @node = run_reactor do
39
+ route :distributor, :zmq_push, rand_addr, :bind
40
+ route :worker1, :zmq_pull, distributor.address, :connect
41
+ route :worker2, :zmq_pull, distributor.address, :connect
42
+ end
43
+ end
44
+
45
+ it_should_behave_like "all routing"
46
+ end
47
+ context "with groups" do
48
+ before(:all) do
49
+ @expected_routing = {
50
+ :distributor_output => {:class => DripDrop::ZMQPushHandler, :socket_ctype => :bind},
51
+ :worker_cluster_worker1 => {:class => DripDrop::ZMQPullHandler, :socket_ctype => :connect},
52
+ :worker_cluster_worker2 => {:class => DripDrop::ZMQPullHandler, :socket_ctype => :connect}
53
+ }
54
+ @node = run_reactor do
55
+ nodelet :distributor do |nlet|
56
+ nlet.route :output, :zmq_push, rand_addr, :bind
57
+ end
58
+ nodelet :worker_cluster do |nlet|
59
+ nlet.route :worker1, :zmq_pull, distributor_output.address, :connect
60
+ nlet.route :worker2, :zmq_pull, distributor_output.address, :connect
61
+ end
62
+ end
63
+ end
64
+
65
+ it_should_behave_like "all routing"
66
+ end
67
+ end
@@ -0,0 +1,98 @@
1
+ require 'spec_helper'
2
+
3
+ describe "websockets" do
4
+ def websockets_send_messages(to_send,&block)
5
+ received = []
6
+ responses = []
7
+ seen_signatures = Set.new
8
+ server = nil
9
+
10
+ open_message = DripDrop::Message.new('open', :body => 'test')
11
+ open_received = false
12
+
13
+ close_occured = false
14
+
15
+ error_occured = false
16
+
17
+ @node = run_reactor(2) do
18
+ addr = rand_addr('ws')
19
+
20
+ server = websocket_server(addr)
21
+ server.on_open do |conn|
22
+ conn.send_message(open_message)
23
+ seen_signatures << conn.signature
24
+ end.on_recv do |message,conn|
25
+ received << message
26
+ conn.send_message(message)
27
+ end.on_close do |conn|
28
+ close_occured = true
29
+ end.on_error do |reason,conn|
30
+ error_occured = true
31
+ end
32
+
33
+ EM.defer do
34
+ client = WebSocket.new(addr)
35
+ open_received = DripDrop::Message.decode(client.receive)
36
+ to_send.each do |message|
37
+ client.send(message.json_encoded)
38
+ end
39
+
40
+ recvd_count = 0
41
+ while recvd_count < to_send.length && (message = client.receive)
42
+ responses << DripDrop::Message.decode_json(message)
43
+ recvd_count += 1
44
+ end
45
+ client.close
46
+
47
+ # This one only connects to test unique signatures
48
+ client2 = WebSocket.new(addr)
49
+ client2.close
50
+ end
51
+
52
+ zmq_subscribe(rand_addr, :bind) {} #Keep zmqmachine happy
53
+ end
54
+
55
+ {:received => received, :responses => responses,
56
+ :open_message => open_message, :open_received => open_received,
57
+ :close_occured => close_occured, :error_occured => error_occured,
58
+ :seen_signatures => seen_signatures,:handlers => {:server => server }}
59
+ end
60
+ describe "basic sending and receiving" do
61
+ before(:all) do
62
+ @sent = []
63
+ 10.times {|i| @sent << DripDrop::Message.new("test-#{i}")}
64
+ @ws_info = websockets_send_messages(@sent)
65
+ end
66
+
67
+ it "should receive all sent messages" do
68
+ recvd_names = @ws_info[:received].map(&:name).inject(Set.new) {|memo,rn| memo << rn}
69
+ @sent.map(&:name).each {|sn| recvd_names.should include(sn)}
70
+ end
71
+
72
+ it "should return to the client as many responses as sent messages" do
73
+ @ws_info[:responses].length.should == @sent.length
74
+ end
75
+
76
+ it "should return to the client an identical message to that which was sent" do
77
+ @ws_info[:received].zip(@ws_info[:responses]).each do |recvd,resp|
78
+ recvd.name.should == resp.name
79
+ end
80
+ end
81
+
82
+ it "should generate an on open message" do
83
+ @ws_info[:open_received].to_hash.should == @ws_info[:open_message].to_hash
84
+ end
85
+
86
+ it "should generate a close event" do
87
+ @ws_info[:close_occured].should be_true
88
+ end
89
+
90
+ it "should not generate an error event" do
91
+ @ws_info[:error_occured].should be_false
92
+ end
93
+
94
+ it "should see unique connection signatures for each client" do
95
+ @ws_info[:seen_signatures].length.should == 2
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,77 @@
1
+ require 'spec_helper'
2
+
3
+ m2_req = '34f9cfef-dc52-4b7f-b197-098765432112 16 /handlertest 537:{"PATH":"/handlertest","accept-language":"en-us,en;q=0.5","user-agent":"Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.13) Gecko/20110207 Gentoo Firefox/3.6.13","host":"it.wishdev.org:6767","accept-charset":"ISO-8859-1,utf-8;q=0.7,*;q=0.7","accept":"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8","keep-alive":"115","x-forwarded-for":"127.0.0.1","cache-control":"max-age=0","connection":"keep-alive","accept-encoding":"gzip,deflate","METHOD":"GET","VERSION":"HTTP/1.1","URI":"/handlertest","PATTERN":"/handlertest"},0:,'
4
+ dd_resp = "34f9cfef-dc52-4b7f-b197-098765432112 2:16, HTTP/1.1 200 OK\r\nContent-Length: 19\r\n\r\nHello from DripDrop"
5
+
6
+ body = ""
7
+ conn_id = "16"
8
+ headers = {"PATH"=>"/handlertest",
9
+ "accept-language"=>"en-us,en;q=0.5",
10
+ "user-agent"=>
11
+ "Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.13) Gecko/20110207 Gentoo Firefox/3.6.13",
12
+ "host"=>"it.wishdev.org:6767",
13
+ "accept-charset"=>"ISO-8859-1,utf-8;q=0.7,*;q=0.7",
14
+ "accept"=>"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
15
+ "keep-alive"=>"115",
16
+ "x-forwarded-for"=>"127.0.0.1",
17
+ "cache-control"=>"max-age=0",
18
+ "connection"=>"keep-alive",
19
+ "accept-encoding"=>"gzip,deflate",
20
+ "METHOD"=>"GET",
21
+ "VERSION"=>"HTTP/1.1",
22
+ "URI"=>"/handlertest",
23
+ "PATTERN"=>"/handlertest"}
24
+ path = "/handlertest"
25
+ sender = "34f9cfef-dc52-4b7f-b197-098765432112"
26
+
27
+ describe "zmq m2" do
28
+ def pp_send_messages(to_send)
29
+ responses = []
30
+ requests = []
31
+
32
+ @node = run_reactor(2) do
33
+ addr = rand_addr
34
+ addr2 = rand_addr
35
+
36
+ m2_send = zmq_push(addr, :bind, {:msg_format => :raw})
37
+ m2_recv = zmq_subscribe(addr2, :bind, {:msg_format => :raw})
38
+
39
+ dd = zmq_m2([addr, addr2])
40
+
41
+ dd.on_recv do |req|
42
+ requests << req
43
+ dd.reply_http req, "Hello from DripDrop"
44
+ end
45
+
46
+ m2_recv.on_recv do |msg|
47
+ responses << msg
48
+ end
49
+
50
+ sleep 1
51
+
52
+ to_send.each {|message| m2_send.send_message(message)}
53
+ end
54
+
55
+ {:responses => responses, :requests => requests}
56
+ end
57
+ describe "basic sending and receiving" do
58
+ before(:all) do
59
+ @sent = [m2_req]
60
+ pp_info = pp_send_messages(@sent)
61
+ @responses = pp_info[:responses]
62
+ @requests = pp_info[:requests]
63
+ end
64
+
65
+ it "should parse a request" do
66
+ @requests[0].body.should == body
67
+ @requests[0].conn_id.should == conn_id
68
+ @requests[0].headers.should == headers
69
+ @requests[0].path.should == path
70
+ @requests[0].sender.should == sender
71
+ end
72
+
73
+ it "should respond to an http request" do
74
+ @responses[0][0].copy_out_string.should == dd_resp
75
+ end
76
+ end
77
+ end