dripdrop 0.10.0-java

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.
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