wamp_client 0.0.8 → 0.0.9
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.
- checksums.yaml +4 -4
- data/.ruby-version +1 -0
- data/LICENSE.txt +1 -1
- data/README.md +56 -14
- data/lib/wamp_client.rb +1 -2
- data/lib/wamp_client/connection.rb +43 -19
- data/lib/wamp_client/session.rb +17 -2
- data/lib/wamp_client/{transport.rb → transport/base.rb} +30 -49
- data/lib/wamp_client/transport/event_machine_base.rb +55 -0
- data/lib/wamp_client/transport/faye_web_socket.rb +83 -0
- data/lib/wamp_client/transport/web_socket_event_machine.rb +86 -0
- data/lib/wamp_client/version.rb +1 -1
- data/spec/connection_spec.rb +150 -0
- data/spec/session_spec.rb +7 -3
- data/spec/spec_helper.rb +105 -6
- data/spec/transport_spec.rb +200 -0
- data/wamp_client.gemspec +7 -4
- metadata +48 -12
@@ -0,0 +1,55 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
Copyright (c) 2016 Eric Chapman
|
4
|
+
|
5
|
+
MIT License
|
6
|
+
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
8
|
+
a copy of this software and associated documentation files (the
|
9
|
+
"Software"), to deal in the Software without restriction, including
|
10
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
11
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
12
|
+
permit persons to whom the Software is furnished to do so, subject to
|
13
|
+
the following conditions:
|
14
|
+
|
15
|
+
The above copyright notice and this permission notice shall be
|
16
|
+
included in all copies or substantial portions of the Software.
|
17
|
+
|
18
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
19
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
20
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
21
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
22
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
23
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
24
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
25
|
+
|
26
|
+
=end
|
27
|
+
|
28
|
+
require 'eventmachine'
|
29
|
+
require_relative 'base'
|
30
|
+
|
31
|
+
module WampClient
|
32
|
+
module Transport
|
33
|
+
class EventMachineBase < Base
|
34
|
+
|
35
|
+
def self.start_event_machine(&block)
|
36
|
+
EM.run do
|
37
|
+
block.call
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.stop_event_machine
|
42
|
+
EM.stop
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.add_timer(milliseconds, &callback)
|
46
|
+
delay = (milliseconds.to_f/1000.0).ceil
|
47
|
+
EM.add_timer(delay) {
|
48
|
+
callback.call
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
@@ -0,0 +1,83 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
Copyright (c) 2016 Eric Chapman
|
4
|
+
|
5
|
+
MIT License
|
6
|
+
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
8
|
+
a copy of this software and associated documentation files (the
|
9
|
+
"Software"), to deal in the Software without restriction, including
|
10
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
11
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
12
|
+
permit persons to whom the Software is furnished to do so, subject to
|
13
|
+
the following conditions:
|
14
|
+
|
15
|
+
The above copyright notice and this permission notice shall be
|
16
|
+
included in all copies or substantial portions of the Software.
|
17
|
+
|
18
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
19
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
20
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
21
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
22
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
23
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
24
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
25
|
+
|
26
|
+
=end
|
27
|
+
|
28
|
+
require_relative 'event_machine_base'
|
29
|
+
|
30
|
+
# This implementation uses the 'faye-websocket' Gem.
|
31
|
+
module WampClient
|
32
|
+
module Transport
|
33
|
+
class FayeWebSocket < EventMachineBase
|
34
|
+
attr_accessor :socket
|
35
|
+
|
36
|
+
def initialize(options)
|
37
|
+
super(options)
|
38
|
+
self.socket = nil
|
39
|
+
|
40
|
+
# Only make them include the gem if they are going to use it
|
41
|
+
require 'faye/websocket'
|
42
|
+
end
|
43
|
+
|
44
|
+
def connect
|
45
|
+
options = { :headers => self.headers }
|
46
|
+
options[:proxy] = self.proxy if self.proxy != nil
|
47
|
+
self.socket = Faye::WebSocket::Client.new(self.uri, [self.protocol], options)
|
48
|
+
|
49
|
+
self.socket.on(:open) do |event|
|
50
|
+
self.connected = true
|
51
|
+
@on_open.call if @on_open
|
52
|
+
end
|
53
|
+
|
54
|
+
self.socket.on(:message) do |event|
|
55
|
+
@on_message.call(self.serializer.deserialize(event.data)) if @on_message
|
56
|
+
end
|
57
|
+
|
58
|
+
self.socket.on(:close) do |event|
|
59
|
+
self.connected = false
|
60
|
+
@on_close.call(event.reason) if @on_close
|
61
|
+
end
|
62
|
+
|
63
|
+
self.socket.on(:error) do |event|
|
64
|
+
@on_error.call(event.message) if @on_error
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def disconnect
|
69
|
+
self.socket.close
|
70
|
+
self.connected = false
|
71
|
+
end
|
72
|
+
|
73
|
+
def send_message(msg)
|
74
|
+
if self.connected
|
75
|
+
self.socket.send(self.serializer.serialize(msg))
|
76
|
+
else
|
77
|
+
raise RuntimeError, "Socket must be open to call 'send_message'"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
Copyright (c) 2016 Eric Chapman
|
4
|
+
|
5
|
+
MIT License
|
6
|
+
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
8
|
+
a copy of this software and associated documentation files (the
|
9
|
+
"Software"), to deal in the Software without restriction, including
|
10
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
11
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
12
|
+
permit persons to whom the Software is furnished to do so, subject to
|
13
|
+
the following conditions:
|
14
|
+
|
15
|
+
The above copyright notice and this permission notice shall be
|
16
|
+
included in all copies or substantial portions of the Software.
|
17
|
+
|
18
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
19
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
20
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
21
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
22
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
23
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
24
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
25
|
+
|
26
|
+
=end
|
27
|
+
|
28
|
+
require_relative 'event_machine_base'
|
29
|
+
|
30
|
+
# This implementation uses the 'websocket-eventmachine-client' Gem.
|
31
|
+
# This is the default if no transport is included
|
32
|
+
module WampClient
|
33
|
+
module Transport
|
34
|
+
class WebSocketEventMachine < EventMachineBase
|
35
|
+
attr_accessor :socket
|
36
|
+
|
37
|
+
def initialize(options)
|
38
|
+
super(options)
|
39
|
+
self.socket = nil
|
40
|
+
|
41
|
+
# Only make them include the gem if they are going to use it
|
42
|
+
require 'websocket-eventmachine-client'
|
43
|
+
|
44
|
+
# Raise an exception if proxy was included (not supported)
|
45
|
+
if self.proxy != nil
|
46
|
+
raise RuntimeError, "The WebSocketEventMachine transport does not support 'proxy'. Try using 'faye-websocket' transport instead"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def connect
|
51
|
+
self.socket = WebSocket::EventMachine::Client.connect(
|
52
|
+
:uri => self.uri,
|
53
|
+
:headers => self.headers
|
54
|
+
)
|
55
|
+
|
56
|
+
self.socket.onopen do
|
57
|
+
self.connected = true
|
58
|
+
@on_open.call if @on_open
|
59
|
+
end
|
60
|
+
|
61
|
+
self.socket.onmessage do |msg, type|
|
62
|
+
@on_message.call(self.serializer.deserialize(msg)) if @on_message
|
63
|
+
end
|
64
|
+
|
65
|
+
self.socket.onclose do |code, reason|
|
66
|
+
self.connected = false
|
67
|
+
@on_close.call(reason) if @on_close
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def disconnect
|
72
|
+
self.connected = !self.socket.close # close returns 'true' if the connection was closed immediately
|
73
|
+
end
|
74
|
+
|
75
|
+
def send_message(msg)
|
76
|
+
if self.connected
|
77
|
+
self.socket.send(self.serializer.serialize(msg), {type: 'text'})
|
78
|
+
else
|
79
|
+
raise RuntimeError, "Socket must be open to call 'send_message'"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
data/lib/wamp_client/version.rb
CHANGED
@@ -0,0 +1,150 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe WampClient::Connection do
|
4
|
+
include RSpec::EM::FakeClock
|
5
|
+
|
6
|
+
before { clock.stub }
|
7
|
+
after { clock.reset }
|
8
|
+
|
9
|
+
before(:each) { SpecHelper::TestTransport.stop_event_machine }
|
10
|
+
|
11
|
+
let(:options) {
|
12
|
+
{
|
13
|
+
uri: 'wss://example.com',
|
14
|
+
realm: 'realm1',
|
15
|
+
transport: SpecHelper::TestTransport,
|
16
|
+
}
|
17
|
+
}
|
18
|
+
let(:connection) { described_class.new(options) }
|
19
|
+
let(:transport) { connection.transport }
|
20
|
+
let(:session) { connection.session }
|
21
|
+
|
22
|
+
def open_connection
|
23
|
+
connection.open
|
24
|
+
clock.tick(1)
|
25
|
+
end
|
26
|
+
|
27
|
+
def open_session_from_server
|
28
|
+
# Send welcome form server
|
29
|
+
welcome = WampClient::Message::Welcome.new(1234, {})
|
30
|
+
transport.receive_message(welcome.payload)
|
31
|
+
end
|
32
|
+
|
33
|
+
def close_session_from_server
|
34
|
+
# Send goodbye from server
|
35
|
+
goodbye = WampClient::Message::Goodbye.new({}, 'felt.like.it')
|
36
|
+
transport.receive_message(goodbye.payload)
|
37
|
+
end
|
38
|
+
|
39
|
+
def check_em_on
|
40
|
+
expect(SpecHelper::TestTransport.event_machine_on?).to eq(true)
|
41
|
+
end
|
42
|
+
|
43
|
+
def check_em_off
|
44
|
+
expect(SpecHelper::TestTransport.event_machine_on?).to eq(false)
|
45
|
+
end
|
46
|
+
|
47
|
+
describe 'transport' do
|
48
|
+
it 'selects the default transport' do
|
49
|
+
connection = described_class.new({})
|
50
|
+
expect(connection.transport_class).to be(WampClient::Transport::WebSocketEventMachine)
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'overrides the default transport' do
|
54
|
+
connection = described_class.new({ transport: WampClient::Transport::FayeWebSocket })
|
55
|
+
expect(connection.transport_class).to be(WampClient::Transport::FayeWebSocket)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'opens the transport/session and sends the hello message' do
|
60
|
+
called = false
|
61
|
+
connection.on(:connect) do
|
62
|
+
called = true
|
63
|
+
end
|
64
|
+
|
65
|
+
open_connection
|
66
|
+
|
67
|
+
expect(transport).not_to be_nil
|
68
|
+
expect(session).not_to be_nil
|
69
|
+
expect(transport.messages.count).to eq(1)
|
70
|
+
expect(transport.messages[0][0]).to eq(WampClient::Message::Types::HELLO)
|
71
|
+
|
72
|
+
expect(called).to eq(true)
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'opens the transport/session and joins' do
|
76
|
+
called = false
|
77
|
+
connection.on(:join) do
|
78
|
+
called = true
|
79
|
+
end
|
80
|
+
|
81
|
+
open_connection
|
82
|
+
open_session_from_server
|
83
|
+
|
84
|
+
check_em_on
|
85
|
+
expect(called).to eq(true)
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'closes the connection' do
|
89
|
+
left = false
|
90
|
+
connection.on(:leave) do
|
91
|
+
left = true
|
92
|
+
end
|
93
|
+
disconnected = false
|
94
|
+
connection.on(:disconnect) do
|
95
|
+
disconnected = true
|
96
|
+
end
|
97
|
+
|
98
|
+
open_connection
|
99
|
+
open_session_from_server
|
100
|
+
|
101
|
+
connection.close
|
102
|
+
|
103
|
+
# Nothing happens until the server responds
|
104
|
+
check_em_on
|
105
|
+
expect(left).to eq(false)
|
106
|
+
expect(disconnected).to eq(false)
|
107
|
+
|
108
|
+
close_session_from_server
|
109
|
+
|
110
|
+
check_em_off
|
111
|
+
expect(left).to eq(true)
|
112
|
+
expect(disconnected).to eq(true)
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'retries if the session is closed from the server' do
|
116
|
+
left = false
|
117
|
+
connection.on(:leave) do
|
118
|
+
left = true
|
119
|
+
end
|
120
|
+
|
121
|
+
joined = false
|
122
|
+
connection.on(:join) do
|
123
|
+
joined = true
|
124
|
+
end
|
125
|
+
|
126
|
+
open_connection
|
127
|
+
open_session_from_server
|
128
|
+
|
129
|
+
check_em_on
|
130
|
+
expect(joined).to eq(true)
|
131
|
+
joined = false
|
132
|
+
expect(left).to eq(false)
|
133
|
+
|
134
|
+
close_session_from_server
|
135
|
+
|
136
|
+
check_em_on
|
137
|
+
expect(joined).to eq(false)
|
138
|
+
expect(left).to eq(true)
|
139
|
+
left = false
|
140
|
+
|
141
|
+
clock.tick(5)
|
142
|
+
open_session_from_server
|
143
|
+
|
144
|
+
check_em_on
|
145
|
+
|
146
|
+
expect(joined).to eq(true)
|
147
|
+
joined = false
|
148
|
+
expect(left).to eq(false)
|
149
|
+
end
|
150
|
+
end
|
data/spec/session_spec.rb
CHANGED
@@ -1115,6 +1115,11 @@ describe WampClient::Session do
|
|
1115
1115
|
end
|
1116
1116
|
|
1117
1117
|
context 'timeout' do
|
1118
|
+
include RSpec::EM::FakeClock
|
1119
|
+
|
1120
|
+
before { clock.stub }
|
1121
|
+
after { clock.reset }
|
1122
|
+
|
1118
1123
|
it 'does not cancel a call if no timeout specified' do
|
1119
1124
|
@defer = WampClient::Defer::ProgressiveCallDefer.new
|
1120
1125
|
|
@@ -1123,7 +1128,7 @@ describe WampClient::Session do
|
|
1123
1128
|
count += 1
|
1124
1129
|
end
|
1125
1130
|
|
1126
|
-
|
1131
|
+
clock.tick(2)
|
1127
1132
|
expect(transport.messages.count).to eq(1)
|
1128
1133
|
end
|
1129
1134
|
|
@@ -1135,8 +1140,7 @@ describe WampClient::Session do
|
|
1135
1140
|
count += 1
|
1136
1141
|
end
|
1137
1142
|
|
1138
|
-
|
1139
|
-
transport.timer_callback.call
|
1143
|
+
clock.tick(2)
|
1140
1144
|
|
1141
1145
|
expect(transport.messages.count).to eq(2)
|
1142
1146
|
|
data/spec/spec_helper.rb
CHANGED
@@ -9,12 +9,13 @@ if ENV['CODECOV_TOKEN']
|
|
9
9
|
end
|
10
10
|
|
11
11
|
require 'wamp_client'
|
12
|
+
require "rspec/em"
|
12
13
|
|
13
14
|
module SpecHelper
|
14
15
|
|
15
|
-
class TestTransport < WampClient::Transport::
|
16
|
-
|
17
|
-
attr_accessor :messages
|
16
|
+
class TestTransport < WampClient::Transport::EventMachineBase
|
17
|
+
@@event_machine_on = false
|
18
|
+
attr_accessor :messages
|
18
19
|
|
19
20
|
def initialize(options)
|
20
21
|
super(options)
|
@@ -23,7 +24,27 @@ module SpecHelper
|
|
23
24
|
end
|
24
25
|
|
25
26
|
def connect
|
27
|
+
self.add_timer(1000) do
|
28
|
+
@on_open.call if @on_open
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def disconnect
|
33
|
+
@connected = false
|
34
|
+
@on_close.call if @on_close
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.start_event_machine(&block)
|
38
|
+
@@event_machine_on = true
|
39
|
+
block.call
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.stop_event_machine
|
43
|
+
@@event_machine_on = false
|
44
|
+
end
|
26
45
|
|
46
|
+
def self.event_machine_on?
|
47
|
+
@@event_machine_on
|
27
48
|
end
|
28
49
|
|
29
50
|
def send_message(msg)
|
@@ -37,13 +58,91 @@ module SpecHelper
|
|
37
58
|
deserialize = self.serializer.deserialize(serialize)
|
38
59
|
|
39
60
|
# Call the received message
|
40
|
-
@on_message.call(deserialize)
|
61
|
+
@on_message.call(deserialize) if @on_message
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
class WebSocketEventMachineClientStub
|
67
|
+
attr_accessor :last_message
|
68
|
+
|
69
|
+
def initialize
|
70
|
+
EM.add_timer(1) {
|
71
|
+
@onopen.call if @onopen != nil
|
72
|
+
}
|
73
|
+
end
|
74
|
+
|
75
|
+
@onopen
|
76
|
+
def onopen(&onopen)
|
77
|
+
@onopen = onopen
|
78
|
+
end
|
79
|
+
|
80
|
+
@onmessage
|
81
|
+
def onmessage(&onmessage)
|
82
|
+
@onmessage = onmessage
|
83
|
+
end
|
84
|
+
|
85
|
+
@onclose
|
86
|
+
def onclose(&onclose)
|
87
|
+
@onclose = onclose
|
41
88
|
end
|
42
89
|
|
43
|
-
def
|
44
|
-
|
90
|
+
def close
|
91
|
+
@onclose.call if @onclose != nil
|
92
|
+
true
|
93
|
+
end
|
94
|
+
|
95
|
+
def send(message, type)
|
96
|
+
self.last_message = message
|
97
|
+
end
|
98
|
+
|
99
|
+
def receive(message)
|
100
|
+
@onmessage.call(message, {type:'text'}) if @onmessage != nil
|
45
101
|
end
|
46
102
|
|
47
103
|
end
|
48
104
|
|
105
|
+
class FayeWebSocketClientStub
|
106
|
+
class Event
|
107
|
+
attr_accessor :data, :reason
|
108
|
+
end
|
109
|
+
|
110
|
+
attr_accessor :last_message
|
111
|
+
|
112
|
+
def initialize
|
113
|
+
EM.add_timer(1) {
|
114
|
+
@on_open.call(Event.new) if @on_open != nil
|
115
|
+
}
|
116
|
+
end
|
117
|
+
|
118
|
+
@on_open
|
119
|
+
@on_message
|
120
|
+
@on_close
|
121
|
+
def on(event, &block)
|
122
|
+
if event == :open
|
123
|
+
@on_open = block
|
124
|
+
elsif event == :close
|
125
|
+
@on_close = block
|
126
|
+
elsif event == :message
|
127
|
+
@on_message = block
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def close
|
132
|
+
event = Event.new
|
133
|
+
event.reason = 'closed'
|
134
|
+
@on_close.call(event) if @on_close != nil
|
135
|
+
end
|
136
|
+
|
137
|
+
def send(message)
|
138
|
+
self.last_message = message
|
139
|
+
end
|
140
|
+
|
141
|
+
def receive(message)
|
142
|
+
event = Event.new
|
143
|
+
event.data = message
|
144
|
+
@on_message.call(event) if @on_message != nil
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
49
148
|
end
|