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.
- data/Gemfile +3 -1
- data/Gemfile.lock +7 -1
- data/VERSION +1 -1
- data/demo/client.html +8 -3
- data/demo/client.js +24 -24
- data/demo/client.rb +29 -0
- data/lib/wamp.rb +11 -0
- data/lib/wamp/bindable.rb +16 -0
- data/lib/wamp/client.rb +118 -0
- data/lib/wamp/engines/memory.rb +104 -0
- data/lib/wamp/protocols/version_1.rb +128 -0
- data/lib/wamp/server.rb +57 -80
- data/lib/wamp/socket.rb +3 -4
- data/lib/wamp/topic.rb +32 -3
- data/spec/lib/wamp/client_spec.rb +9 -0
- data/spec/lib/wamp/engines/memory_spec.rb +118 -0
- data/spec/lib/wamp/protocols/version_1_spec.rb +80 -0
- data/spec/lib/wamp/server_spec.rb +5 -6
- data/spec/lib/wamp/socket_spec.rb +6 -6
- data/spec/lib/wamp/topic_spec.rb +1 -1
- data/spec/support/dummy_socket.rb +11 -0
- data/wamp.gemspec +20 -5
- metadata +49 -8
@@ -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
|
-
|
6
|
+
include WAMP::Bindable
|
7
7
|
|
8
|
-
|
8
|
+
attr_accessor :options, :topics, :callbacks
|
9
9
|
|
10
10
|
def initialize(options = {})
|
11
|
-
@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
|
-
|
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
|
-
|
44
|
+
client = @engine.create_client(websocket)
|
45
|
+
client.websocket.send @protocol.welcome(client.id)
|
56
46
|
|
57
|
-
trigger(:connect,
|
47
|
+
trigger(:connect, client)
|
58
48
|
end
|
59
49
|
|
60
50
|
def handle_message(websocket, event)
|
61
|
-
|
51
|
+
client = @engine.find_clients(websocket: websocket).first
|
62
52
|
|
63
|
-
|
64
|
-
msg_type
|
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
|
-
|
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
|
-
|
60
|
+
handle_call(client, data)
|
76
61
|
when :SUBSCRIBE
|
77
|
-
handle_subscribe(
|
62
|
+
handle_subscribe(client, data)
|
78
63
|
when :UNSUBSCRIBE
|
79
|
-
|
80
|
-
topic = @topics[topic_name]
|
81
|
-
|
82
|
-
trigger(:unsubscribe, socket, topic.name)
|
64
|
+
handle_unsubscribe(client, data)
|
83
65
|
when :PUBLISH
|
84
|
-
handle_publish(
|
66
|
+
handle_publish(client, data)
|
85
67
|
end
|
86
68
|
end
|
87
69
|
|
88
|
-
# Handle a
|
89
|
-
#
|
90
|
-
def
|
91
|
-
|
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
|
-
|
94
|
-
|
75
|
+
topic = @engine.find_or_create_topic(uri)
|
76
|
+
client.add_prefix(prefix, topic)
|
95
77
|
|
96
|
-
trigger(:
|
78
|
+
trigger(:prefix, client, prefix, uri)
|
97
79
|
end
|
98
80
|
|
99
|
-
# Handle
|
100
|
-
#
|
101
|
-
def
|
102
|
-
|
103
|
-
|
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
|
-
|
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(:
|
94
|
+
trigger(:subscribe, client, topic.uri)
|
108
95
|
end
|
109
96
|
|
110
|
-
# Handle
|
111
|
-
#
|
112
|
-
def
|
113
|
-
topic
|
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
|
-
|
125
|
-
|
126
|
-
next if exclude.include? k
|
102
|
+
trigger(:unsubscribe, client, topic.uri)
|
103
|
+
end
|
127
104
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
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
|
-
|
111
|
+
topic.publish(client, @protocol, payload, exclude, include)
|
134
112
|
|
135
|
-
trigger(:publish,
|
113
|
+
trigger(:publish, client, topic.uri, payload, exclude, include)
|
136
114
|
end
|
137
115
|
|
138
116
|
def handle_close(websocket, event)
|
139
|
-
|
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,
|
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 =
|
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 :
|
3
|
+
attr_accessor :uri, :clients
|
4
4
|
|
5
|
-
def initialize(
|
6
|
-
@
|
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,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
|