wamp_client 0.0.8 → 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- 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
|