wamp 0.0.0 → 0.0.1

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