wamp 0.0.0 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,128 @@
1
+ require 'json'
2
+
3
+ module WAMP::Protocols
4
+
5
+ # Describes the WAMP protocol messages per http://www.wamp.ws/spec#call_message
6
+ class Version1
7
+ # @return [Integer] The version of the WAMP protocol defined in this class.
8
+ def version
9
+ 1
10
+ end
11
+
12
+ # Builds the WELCOME message (server to client)
13
+ # [ TYPE_ID_WELCOME , sessionId , protocolVersion, serverIdent ]
14
+ # @param client_id [String] The server generated ID that the WELCOME message
15
+ # is to be sent to.
16
+ # @return [String] The WELCOME message as a JSON string.
17
+ def welcome(id)
18
+ [type[:WELCOME], id, version, server_ident].to_json
19
+ end
20
+
21
+ # Builds the PREFIX message (client to server)
22
+ # [ TYPE_ID_PREFIX , prefix , URI ]
23
+ # @param prefix [String] The shortened CURIE prefix to be registered.
24
+ # @param uri [String] The full URI to register the prefix to.
25
+ # @return [String] The PREFIX message as a JSON string.
26
+ def prefix(prefix, uri)
27
+ [type[:PREFIX], prefix, uri].to_json
28
+ end
29
+
30
+ # Builds the RPC CALL message (client to server)
31
+ # [ TYPE_ID_CALL , callID , procURI , ... ]
32
+ # @param call_id [String] This should be a randomly generated string to
33
+ # identify the RPC call, the call ID will be returned to the client in the
34
+ # RPC CALLRESULT or CALLERROR messages.
35
+ # @param proc_uri [String] The procURI is a string that identifies the remote
36
+ # procedure to be called and MUST be a valid URI or CURIE.
37
+ # @param *args [Array] Zero or more additional arguments to be sent with the
38
+ # CALL message.
39
+ # @return [String] The CALL message as a JSON string
40
+ def call(call_id, proc_uri, *args)
41
+ [type[:CALL], call_id, proc_uri, *args].to_json
42
+ end
43
+
44
+ # Builds the RPC CALLRESULT message (server to client)
45
+ # [ TYPE_ID_CALLRESULT , callID , result ]
46
+ # @param call_id [String] This should match the call ID given by the client
47
+ # by the client.
48
+ # @param result [String, Integer, Hash, Array] The results of the RPC procedure
49
+ # initiated by the CALL. Defaults to nil of non is given.
50
+ # @return [String] The CALLRESULT message as a JSON string.
51
+ def call_result(call_id, result = nil)
52
+ [type[:CALLRESULT], call_id, result].to_json
53
+ end
54
+
55
+ # Builds the RPC CALLERROR message (server to client)
56
+ # [ TYPE_ID_CALLERROR , callID , errorURI , errorDesc , errorDetails(Optional) ]
57
+ # @param call_id [String] This should match the call ID given by the client
58
+ # by the client.
59
+ # @param error_uri [String] A CURIE or URI identifying the error.
60
+ # @param error_desc [String] A description of the error that occured.
61
+ # @param error_details [String] Optional. Used to communicate application
62
+ # error details, defined by the error_uri.
63
+ # @return [String] The ERRORRESULT message as a JSON string.
64
+ def call_error(call_id, error_uri, error_desc, error_details = nil)
65
+ msg = [type[:CALLERROR], call_id, error_uri, error_desc, error_details]
66
+ msg.delete_if { |x| x.nil? }
67
+ msg.to_json
68
+ end
69
+
70
+ # Builds the PubSub SUBSCRIBE message (client to server)
71
+ # [ TYPE_ID_SUBSCRIBE , topicURI ]
72
+ # @param topic_uri [String] The topic URI or CURIE (from PREFIX) to receive
73
+ # published events to the given topic.
74
+ # @return [String] The SUBSCRIBE message as a JSON string.
75
+ def subscribe(topic_uri)
76
+ [type[:SUBSCRIBE], topic_uri].to_json
77
+ end
78
+
79
+ # Builds the PubSub UNSUBSCRIBE message (client to server)
80
+ # [ TYPE_ID_UNSUBSCRIBE , topicURI ]
81
+ # @param topic_uri [String] The topic URI or CURIE to unsubscribe from.
82
+ # @return [String] The UNSUBSCRIBE message as a JSON string.
83
+ def unsubscribe(topic_uri)
84
+ [type[:UNSUBSCRIBE], topic_uri].to_json
85
+ end
86
+
87
+ # Builds the PubSub PUBLISH message (client to server)
88
+ # [ TYPE_ID_PUBLISH , topicURI , event ]
89
+ # [ TYPE_ID_PUBLISH , topicURI , event , excludeMe ]
90
+ # [ TYPE_ID_PUBLISH , topicURI , event , exclude , eligible ]
91
+ # @param topic_uri [String] The topic URI or CURIE to publish the event to.
92
+ # @param payload [String, Array, Hash] The payload to be delivered to the
93
+ # server.
94
+ # @param exclude [true, false, Array<String>] Optional. Determines which
95
+ # clients to exclude from the delivery of the event from the server, you
96
+ # can give true or false will exclude/include the sending client, or give
97
+ # an Array of client ID's to exclude.
98
+ # @param elgible [Array<String>] Optional. An array lf client ID's elgible
99
+ # to receive the published message.
100
+ # @return [String] The PUBLISH message as a JSON string.
101
+ def publish(topic_uri, event, exclude = nil, elgible = nil)
102
+ msg = [type[:PUBLISH], topic_uri, event]
103
+ msg[3] = exclude unless exclude.nil?
104
+ msg[4] = elgible unless elgible.nil?
105
+
106
+ msg.to_json
107
+ end
108
+
109
+ # Builds the PubSub EVENT message (server to client)
110
+ # [ TYPE_ID_EVENT , topicURI , event ]
111
+ # @param topic_uri [String] The topic URI or CURIE to publish the event to.
112
+ # @param event [String, Array, Hash] The payload to be delivered to the clients
113
+ # @return [String] The EVENT message as a JSON string.
114
+ def event(topic_uri, event)
115
+ [type[:EVENT], topic_uri, event].to_json
116
+ end
117
+
118
+ private
119
+
120
+ def server_ident
121
+ WAMP.identity
122
+ end
123
+
124
+ def type
125
+ WAMP::MessageType
126
+ end
127
+ end
128
+ end
data/lib/wamp/server.rb CHANGED
@@ -3,21 +3,26 @@ require 'json'
3
3
 
4
4
  module WAMP
5
5
  class Server
6
- attr_accessor :options, :sockets, :topics
6
+ include WAMP::Bindable
7
7
 
8
- CALLBACKS = [:subscribe, :unsubscribe, :publish, :call, :prefix, :connect, :disconnect]
8
+ attr_accessor :options, :topics, :callbacks
9
9
 
10
10
  def initialize(options = {})
11
- @options = options
12
- @sockets = {}
13
- @topics = {}
11
+ @options = options
14
12
 
13
+ @topics = {}
15
14
  @callbacks = {}
15
+ @engine = WAMP::Engines::Memory.new
16
+ @protocol = WAMP::Protocols::Version1.new
17
+ end
18
+
19
+ def available_bindings
20
+ [:subscribe, :unsubscribe, :publish, :call, :prefix, :connect, :disconnect]
16
21
  end
17
22
 
18
23
  def start
19
24
  lambda do |env|
20
- # Faye::WebSocket.load_adapter('thin')
25
+ Faye::WebSocket.load_adapter('thin')
21
26
  if Faye::WebSocket.websocket?(env)
22
27
  ws = Faye::WebSocket.new(env, ['wamp'], ping: 25)
23
28
 
@@ -33,114 +38,86 @@ module WAMP
33
38
  end
34
39
  end
35
40
 
36
- def bind(name, &callback)
37
- raise "Invalid callback name: #{name}" unless CALLBACKS.include? name
38
- @callbacks[name] = callback
39
- end
40
-
41
- def trigger(name, *args)
42
- @callbacks[name].call *args if @callbacks[name]
43
- end
44
-
45
- def send_event_to_all
46
- msg = [8, 'ws://localhost:9292/', "Here I am, Rock you like a hurricane"]
47
- @sockets.each_pair do |s, c|
48
- s.send msg.to_json
49
- end
50
- end
51
-
52
41
  private
53
42
 
54
43
  def handle_open(websocket, event)
55
- socket = @sockets[websocket] = WAMP::Socket.new(websocket)
44
+ client = @engine.create_client(websocket)
45
+ client.websocket.send @protocol.welcome(client.id)
56
46
 
57
- trigger(:connect, socket)
47
+ trigger(:connect, client)
58
48
  end
59
49
 
60
50
  def handle_message(websocket, event)
61
- socket = @sockets[websocket]
51
+ client = @engine.find_clients(websocket: websocket).first
62
52
 
63
- parsed_msg = JSON.parse(event.data)
64
- msg_type = parsed_msg[0]
53
+ data = JSON.parse(event.data)
54
+ msg_type = data.shift
65
55
 
66
56
  case WAMP::MessageType[msg_type]
67
57
  when :PREFIX
68
- prefix = parsed_msg[1]
69
- uri = parsed_msg[2]
70
-
71
- socket.add_prefix(prefix, uri)
72
-
73
- trigger(:prefix, socket, prefix, uri)
58
+ handle_prefix(client, data)
74
59
  when :CALL
75
- # TODO handle RPC Call
60
+ handle_call(client, data)
76
61
  when :SUBSCRIBE
77
- handle_subscribe(socket, parsed_msg)
62
+ handle_subscribe(client, data)
78
63
  when :UNSUBSCRIBE
79
- topic_name = parsed_msg[1]
80
- topic = @topics[topic_name]
81
-
82
- trigger(:unsubscribe, socket, topic.name)
64
+ handle_unsubscribe(client, data)
83
65
  when :PUBLISH
84
- handle_publish(socket, parsed_msg)
66
+ handle_publish(client, data)
85
67
  end
86
68
  end
87
69
 
88
- # Handle a subscribe message from a client
89
- # SUBSCRIBE data structure [5, TOPIC]
90
- def handle_subscribe(socket, data)
91
- topic_uri = data[1]
70
+ # Handle a prefix message from a client
71
+ # PREFIX data structure [PREFIX, URI]
72
+ def handle_prefix(client, data)
73
+ prefix, uri = data
92
74
 
93
- @topics[topic_uri] ||= WAMP::Topic.new(topic_uri)
94
- socket.add_topic(@topics[topic_uri])
75
+ topic = @engine.find_or_create_topic(uri)
76
+ client.add_prefix(prefix, topic)
95
77
 
96
- trigger(:subscribe, socket, topic.name)
78
+ trigger(:prefix, client, prefix, uri)
97
79
  end
98
80
 
99
- # Handle an unsubscribe message from client
100
- # UNSUBSCRIBE data structure [6, TOPIC]
101
- def handle_unsubscribe(socket, data)
102
- topic_uri = data[1]
103
- topic = @topics[topic_uri]
81
+ # Handle RPC call message from a client
82
+ # CALL data structure [callID, procURI, ... ]
83
+ def handle_call(client, data)
84
+ call_id, proc_uri, *args = data
85
+
86
+ trigger(:call, client, call_id, proc_uri, args)
87
+ end
104
88
 
105
- socket.remove_topic(topic) if topic
89
+ # Handle a subscribe message from a client
90
+ # SUBSCRIBE data structure [TOPIC]
91
+ def handle_subscribe(client, data)
92
+ topic = @engine.subscribe_client_to_topic client, data[0]
106
93
 
107
- trigger(:unsubscribe, socket, topic.name)
94
+ trigger(:subscribe, client, topic.uri)
108
95
  end
109
96
 
110
- # Handle a message published by a client
111
- # PUBLISH data structure [7, TOPIC, DATA, EXCLUDE, INCLUDE]
112
- def handle_publish(socket, data)
113
- topic = topics[data[1]]
114
- payload = data[2]
115
- exclude = data[3]
116
- include = data[4]
117
-
118
- if exclude == true
119
- exclude = [socket.id]
120
- elsif exclude == false
121
- exclude = []
122
- end
97
+ # Handle an unsubscribe message from client
98
+ # UNSUBSCRIBE data structure [TOPIC]
99
+ def handle_unsubscribe(client, data)
100
+ topic = @engine.unsubscribe_client_from_topic(client, data[0])
123
101
 
124
- # Send payload to all sockets subscribed to topic
125
- @sockets.each_pair do |k, v|
126
- next if exclude.include? k
102
+ trigger(:unsubscribe, client, topic.uri)
103
+ end
127
104
 
128
- if v.topics.include? topic
129
- k.send [WAMP::MessageType[:EVENT], topic.name, payload].to_json
130
- end
131
- end
105
+ # Handle a message published by a client
106
+ # PUBLISH data structure [TOPIC, DATA, EXCLUDE, INCLUDE]
107
+ def handle_publish(client, data)
108
+ topic_name, payload, exclude, include = data
109
+ topic = @engine.find_or_create_topic(topic_name)
132
110
 
133
- # Todo: Filter send with include
111
+ topic.publish(client, @protocol, payload, exclude, include)
134
112
 
135
- trigger(:publish, socket, topic.name, payload, exclude, include)
113
+ trigger(:publish, client, topic.uri, payload, exclude, include)
136
114
  end
137
115
 
138
116
  def handle_close(websocket, event)
139
- socket = @sockets.delete(websocket)
140
-
141
- p [socket.id, :close, event.code, event.reason]
117
+ # client = @engine.find_clients(websocket: websocket).first
118
+ client = @engine.delete_client(websocket)
142
119
 
143
- trigger(:disconnect, socket)
120
+ trigger(:disconnect, client)
144
121
  end
145
122
  end
146
123
  end
data/lib/wamp/socket.rb CHANGED
@@ -1,12 +1,11 @@
1
- require 'securerandom'
2
1
  require 'json'
3
2
 
4
3
  module WAMP
5
4
  class Socket
6
5
  attr_accessor :id, :websocket, :topics, :prefixes
7
6
 
8
- def initialize(websocket)
9
- @id = SecureRandom.uuid
7
+ def initialize(id, websocket)
8
+ @id = id
10
9
  @topics = []
11
10
  @prefixes = {}
12
11
  @websocket = websocket
@@ -30,7 +29,7 @@ module WAMP
30
29
 
31
30
  def send_welcome_message
32
31
  welcome_msg = [WAMP::MessageType[:WELCOME], id, WAMP.protocol_version, WAMP.identity]
33
- websocket.send welcome_msg.to_json
32
+ # websocket.send welcome_msg.to_json
34
33
  end
35
34
  end
36
35
  end
data/lib/wamp/topic.rb CHANGED
@@ -1,9 +1,38 @@
1
1
  module WAMP
2
2
  class Topic
3
- attr_accessor :name
3
+ attr_accessor :uri, :clients
4
4
 
5
- def initialize(name)
6
- @name = name
5
+ def initialize(uri)
6
+ @uri = uri
7
+ @clients = []
8
+ end
9
+
10
+ def add_client(client)
11
+ clients << client unless clients.include? client
12
+ end
13
+
14
+ def remove_client(client)
15
+ clients.delete client
16
+ end
17
+
18
+ def publish(client, protocol, payload, excluded, included)
19
+ rec_clients = clients.dup
20
+
21
+ if excluded == true
22
+ excluded = [client.id]
23
+ elsif excluded == false || excluded.nil?
24
+ excluded = []
25
+ end
26
+
27
+ rec_clients.delete_if { |c| excluded.include? c.id }
28
+
29
+ if included
30
+ rec_clients.delete_if { |c| !included.include?(c.id) }
31
+ end
32
+
33
+ rec_clients.each do |c|
34
+ c.websocket.send protocol.event(self.uri, payload)
35
+ end
7
36
  end
8
37
  end
9
38
  end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe WAMP::Client do
4
+ let(:client) { WAMP::Client.new }
5
+
6
+ it "should start with an empty id" do
7
+ expect(client.id).to be_nil
8
+ end
9
+ end
@@ -0,0 +1,118 @@
1
+ require 'spec_helper'
2
+
3
+ describe WAMP::Engines::Memory do
4
+ let(:memory) { WAMP::Engines::Memory.new }
5
+
6
+ context "#initialization" do
7
+ it "should initialize with some empty hashes" do
8
+ expect(memory.clients).to eq({})
9
+ expect(memory.topics).to eq({})
10
+ expect(memory.options).to eq({})
11
+ end
12
+ end
13
+
14
+ context "client management" do
15
+ context "#create_client" do
16
+ it "should create a new instance of a socket" do
17
+ expect { memory.create_client(DummySocket) }
18
+ .to change(memory.clients, :size).by(1)
19
+ end
20
+ end
21
+
22
+ context "with existing clients" do
23
+ before do
24
+ @ds1 = DummySocket.new(1)
25
+ @ds2 = DummySocket.new(2)
26
+
27
+ @id1 = memory.create_client(@ds1).id
28
+ @id2 = memory.create_client(@ds2).id
29
+ end
30
+
31
+ context "#delete_client" do
32
+ it "should delete a client" do
33
+ expect { memory.delete_client @ds1 }
34
+ .to change(memory.clients, :size).by(-1)
35
+ end
36
+
37
+ it "should return the removed client" do
38
+ client = memory.find_clients(websocket: @ds1).first
39
+ expect(memory.delete_client @ds1).to eq client
40
+ end
41
+ end
42
+
43
+ context "#all_clients" do
44
+ it "should return an array of all the clients" do
45
+ expect(memory.all_clients.size).to eq 2
46
+
47
+ expect(memory.all_clients).to eq memory.clients.values
48
+ end
49
+ end
50
+
51
+ context "#find_clients" do
52
+ it "should find a client by the given parameter" do
53
+ expect(memory.find_clients(id: @id1).first.websocket).to eq @ds1
54
+ expect(memory.find_clients(id: @id2).first.websocket).to eq @ds2
55
+ end
56
+
57
+ it "should return all the clients that match" do
58
+ expect(memory.find_clients(topics: []).size).to eq 2
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ context "topic management" do
65
+ context "find or create a new topic" do
66
+ it "should create a new topic" do
67
+ expect { memory.find_or_create_topic("http://localhost/sample_topic") }
68
+ .to change(memory.topics, :size).by(1)
69
+ end
70
+
71
+ it "should return an existing topic if one already exists" do
72
+ new_topic = memory.find_or_create_topic("http://localhost/sample_topic")
73
+ expect(memory.find_or_create_topic "http://localhost/sample_topic")
74
+ .to eq new_topic
75
+ end
76
+ end
77
+
78
+ context "topic subscription" do
79
+ before do
80
+ @client = memory.create_client(@ds1)
81
+ @topic = memory.find_or_create_topic("http://localhost/sample_topic")
82
+
83
+ @sub = memory.subscribe_client_to_topic @client, @topic.uri
84
+ end
85
+
86
+ it "should add a client to a topic" do
87
+ expect(@client.topics).to include @topic
88
+ end
89
+
90
+ it "should add a topic to a client" do
91
+ expect(@topic.clients).to include @client
92
+ end
93
+
94
+ it "should return the topic the client subscribed to" do
95
+ expect(@sub).to eq @topic
96
+ end
97
+ end
98
+
99
+ context "topic unsubscription" do
100
+ before do
101
+ @client = memory.create_client(@ds1)
102
+ @topic = memory.find_or_create_topic("http://localhost/sample_topic")
103
+
104
+ @sub = memory.subscribe_client_to_topic @client, @topic.uri
105
+ end
106
+
107
+ it "should remove the client from the topic" do
108
+ expect { memory.unsubscribe_client_from_topic @client, @topic.uri }
109
+ .to change(@topic.clients, :size).by(-1)
110
+ end
111
+
112
+ it "should remove the topic from the client" do
113
+ expect { memory.unsubscribe_client_from_topic @client, @topic.uri }
114
+ .to change(@client.topics, :size).by(-1)
115
+ end
116
+ end
117
+ end
118
+ end