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.
@@ -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
+
@@ -26,5 +26,5 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26
26
  =end
27
27
 
28
28
  module WampClient
29
- VERSION = '0.0.8'
29
+ VERSION = '0.0.9'
30
30
  end
@@ -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
- expect(transport.timer_callback).to be_nil
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
- expect(transport.timer_callback).not_to be_nil
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::Base
16
-
17
- attr_accessor :messages, :timer_callback
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) unless @on_message.nil?
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 timer(milliseconds, &callback)
44
- self.timer_callback = callback
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