em-websocket 0.3.5 → 0.3.6

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.
@@ -1,5 +1,11 @@
1
1
  = Changelog
2
2
 
3
+ == 0.3.6 / 2011-12-23
4
+
5
+ - new features:
6
+ - Supports sending ping & pong messages
7
+ - Supports binding to received ping & pong messages
8
+
3
9
  == 0.3.5 / 2011-10-24
4
10
 
5
11
  - new features:
@@ -1,8 +1,8 @@
1
- require 'lib/em-websocket'
1
+ require File.expand_path('../../lib/em-websocket', __FILE__)
2
2
 
3
3
  EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 8080, :debug => true) do |ws|
4
4
  ws.onopen { ws.send "Hello Client!"}
5
5
  ws.onmessage { |msg| ws.send "Pong: #{msg}" }
6
6
  ws.onclose { puts "WebSocket closed" }
7
7
  ws.onerror { |e| puts "Error: #{e.message}" }
8
- end
8
+ end
@@ -0,0 +1,24 @@
1
+ require File.expand_path('../../lib/em-websocket', __FILE__)
2
+
3
+ EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 8080, :debug => false) do |ws|
4
+ timer = nil
5
+ ws.onopen {
6
+ puts "Ping supported: #{ws.pingable?}"
7
+ timer = EM.add_periodic_timer(1) {
8
+ p ["Sent ping", ws.ping('hello')]
9
+ }
10
+ }
11
+ ws.onpong { |value|
12
+ puts "Received pong: #{value}"
13
+ }
14
+ ws.onping { |value|
15
+ puts "Received ping: #{value}"
16
+ }
17
+ ws.onclose {
18
+ EM.cancel_timer(timer)
19
+ puts "WebSocket closed"
20
+ }
21
+ ws.onerror { |e|
22
+ puts "Error: #{e.message}"
23
+ }
24
+ end
@@ -10,6 +10,8 @@ module EventMachine
10
10
  def onclose(&blk); @onclose = blk; end
11
11
  def onerror(&blk); @onerror = blk; end
12
12
  def onmessage(&blk); @onmessage = blk; end
13
+ def onping(&blk); @onping = blk; end
14
+ def onpong(&blk); @onpong = blk; end
13
15
 
14
16
  def trigger_on_message(msg)
15
17
  @onmessage.call(msg) if @onmessage
@@ -20,6 +22,12 @@ module EventMachine
20
22
  def trigger_on_close
21
23
  @onclose.call if @onclose
22
24
  end
25
+ def trigger_on_ping(data)
26
+ @onping.call(data) if @onping
27
+ end
28
+ def trigger_on_pong(data)
29
+ @onpong.call(data) if @onpong
30
+ end
23
31
  def trigger_on_error(reason)
24
32
  return false unless @onerror
25
33
  @onerror.call(reason)
@@ -140,6 +148,43 @@ module EventMachine
140
148
  end
141
149
  end
142
150
 
151
+ # Send a ping to the client. The client must respond with a pong.
152
+ #
153
+ # In the case that the client is running a WebSocket draft < 01, false
154
+ # is returned since ping & pong are not supported
155
+ #
156
+ def ping(body = '')
157
+ if @handler
158
+ @handler.pingable? ? @handler.send_frame(:ping, body) && true : false
159
+ else
160
+ raise WebSocketError, "Cannot ping before onopen callback"
161
+ end
162
+ end
163
+
164
+ # Send an unsolicited pong message, as allowed by the protocol. The
165
+ # client is not expected to respond to this message.
166
+ #
167
+ # em-websocket automatically takes care of sending pong replies to
168
+ # incoming ping messages, as the protocol demands.
169
+ #
170
+ def pong(body = '')
171
+ if @handler
172
+ @handler.pingable? ? @handler.send_frame(:pong, body) && true : false
173
+ else
174
+ raise WebSocketError, "Cannot ping before onopen callback"
175
+ end
176
+ end
177
+
178
+ # Test whether the connection is pingable (i.e. the WebSocket draft in
179
+ # use is >= 01)
180
+ def pingable?
181
+ if @handler
182
+ @handler.pingable?
183
+ else
184
+ raise WebSocketError, "Cannot test whether pingable before onopen callback"
185
+ end
186
+ end
187
+
143
188
  def request
144
189
  @handler ? @handler.request : {}
145
190
  end
@@ -36,6 +36,16 @@ module EventMachine
36
36
  @state = :closed
37
37
  @connection.trigger_on_close
38
38
  end
39
+
40
+ def ping
41
+ # Overridden in subclass
42
+ false
43
+ end
44
+
45
+ def pingable?
46
+ # Also Overridden
47
+ false
48
+ end
39
49
  end
40
50
  end
41
51
  end
@@ -22,8 +22,9 @@ module EventMachine
22
22
  when :ping
23
23
  # Pong back the same data
24
24
  send_frame(:pong, application_data)
25
+ @connection.trigger_on_ping(application_data)
25
26
  when :pong
26
- # TODO: Do something. Complete a deferrable established by a ping?
27
+ @connection.trigger_on_pong(application_data)
27
28
  when :text
28
29
  if application_data.respond_to?(:force_encoding)
29
30
  application_data.force_encoding("UTF-8")
@@ -33,6 +34,11 @@ module EventMachine
33
34
  @connection.trigger_on_message(application_data)
34
35
  end
35
36
  end
37
+
38
+ # Ping & Pong supported
39
+ def pingable?
40
+ true
41
+ end
36
42
  end
37
43
  end
38
44
  end
@@ -35,8 +35,9 @@ module EventMachine
35
35
  when :ping
36
36
  # Pong back the same data
37
37
  send_frame(:pong, application_data)
38
+ @connection.trigger_on_ping(application_data)
38
39
  when :pong
39
- # TODO: Do something. Complete a deferrable established by a ping?
40
+ @connection.trigger_on_pong(application_data)
40
41
  when :text
41
42
  if application_data.respond_to?(:force_encoding)
42
43
  application_data.force_encoding("UTF-8")
@@ -46,6 +47,11 @@ module EventMachine
46
47
  @connection.trigger_on_message(application_data)
47
48
  end
48
49
  end
50
+
51
+ # Ping & Pong supported
52
+ def pingable?
53
+ true
54
+ end
49
55
  end
50
56
  end
51
57
  end
@@ -1,5 +1,5 @@
1
1
  module EventMachine
2
2
  module Websocket
3
- VERSION = "0.3.5"
3
+ VERSION = "0.3.6"
4
4
  end
5
5
  end
@@ -132,11 +132,6 @@ def format_response(r)
132
132
  data
133
133
  end
134
134
 
135
- def handler(request, secure = false)
136
- connection = Object.new
137
- EM::WebSocket::HandlerFactory.build(connection, format_request(request), secure)
138
- end
139
-
140
135
  RSpec::Matchers.define :send_handshake do |response|
141
136
  match do |actual|
142
137
  actual.handshake.lines.sort == format_response(response).lines.sort
@@ -47,19 +47,48 @@ describe "draft13" do
47
47
 
48
48
  it "should send back the correct handshake response" do
49
49
  em {
50
- EM.add_timer(0.1) do
51
- EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { }
52
-
53
- # Create a fake client which sends draft 07 handshake
54
- connection = EM.connect('0.0.0.0', 12345, Draft07FakeWebSocketClient)
55
- connection.send_data(format_request(@request))
56
-
57
- connection.onopen {
58
- connection.handshake_response.lines.sort.
59
- should == format_response(@response).lines.sort
50
+ EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { }
51
+
52
+ # Create a fake client which sends draft 13 handshake
53
+ connection = EM.connect('0.0.0.0', 12345, Draft07FakeWebSocketClient)
54
+ connection.send_data(format_request(@request))
55
+
56
+ connection.onopen {
57
+ connection.handshake_response.lines.sort.
58
+ should == format_response(@response).lines.sort
59
+ done
60
+ }
61
+ }
62
+ end
63
+
64
+ # TODO: This test would be much nicer with a real websocket client...
65
+ it "should support sending pings and binding to onpong" do
66
+ em {
67
+ EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
68
+ ws.onopen {
69
+ ws.should be_pingable
70
+ EM.next_tick {
71
+ ws.ping('hello').should == true
72
+ }
73
+
74
+ }
75
+ ws.onpong { |data|
76
+ data.should == 'hello'
60
77
  done
61
78
  }
62
- end
79
+ }
80
+
81
+ # Create a fake client which sends draft 13 handshake
82
+ connection = EM.connect('0.0.0.0', 12345, Draft07FakeWebSocketClient)
83
+ connection.send_data(format_request(@request))
84
+
85
+ # Confusing, fake onmessage means any data after the handshake
86
+ connection.onmessage { |data|
87
+ # This is what a ping looks like
88
+ data.should == "\x89\x05hello"
89
+ # This is what a pong looks like
90
+ connection.send_data("\x8a\x05hello")
91
+ }
63
92
  }
64
93
  end
65
94
  end
@@ -50,97 +50,89 @@ describe "WebSocket server draft76" do
50
50
 
51
51
  it "should send back the correct handshake response" do
52
52
  em {
53
- EM.add_timer(0.1) do
54
- EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { }
55
-
56
- # Create a fake client which sends draft 76 handshake
57
- connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
58
- connection.send_data(format_request(@request))
59
-
60
- connection.onopen {
61
- connection.handshake_response.lines.sort.
62
- should == format_response(@response).lines.sort
63
- done
64
- }
65
- end
53
+ EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { }
54
+
55
+ # Create a fake client which sends draft 76 handshake
56
+ connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
57
+ connection.send_data(format_request(@request))
58
+
59
+ connection.onopen {
60
+ connection.handshake_response.lines.sort.
61
+ should == format_response(@response).lines.sort
62
+ done
63
+ }
66
64
  }
67
65
  end
68
66
 
69
67
  it "should send closing frame back and close the connection after recieving closing frame" do
70
68
  em {
71
- EM.add_timer(0.1) do
72
- EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { }
73
-
74
- # Create a fake client which sends draft 76 handshake
75
- connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
76
- connection.send_data(format_request(@request))
77
-
78
- # Send closing frame after handshake complete
79
- connection.onopen {
80
- connection.send_data(EM::WebSocket::Handler76::TERMINATE_STRING)
81
- }
82
-
83
- # Check that this causes a termination string to be returned and the
84
- # connection close
85
- connection.onclose {
86
- connection.packets[0].should ==
87
- EM::WebSocket::Handler76::TERMINATE_STRING
88
- done
89
- }
90
- end
69
+ EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { }
70
+
71
+ # Create a fake client which sends draft 76 handshake
72
+ connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
73
+ connection.send_data(format_request(@request))
74
+
75
+ # Send closing frame after handshake complete
76
+ connection.onopen {
77
+ connection.send_data(EM::WebSocket::Handler76::TERMINATE_STRING)
78
+ }
79
+
80
+ # Check that this causes a termination string to be returned and the
81
+ # connection close
82
+ connection.onclose {
83
+ connection.packets[0].should ==
84
+ EM::WebSocket::Handler76::TERMINATE_STRING
85
+ done
86
+ }
91
87
  }
92
88
  end
93
89
 
94
90
  it "should ignore any data received after the closing frame" do
95
91
  em {
96
- EM.add_timer(0.1) do
97
- EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
98
- # Fail if foobar message is received
99
- ws.onmessage { |msg|
100
- fail
101
- }
102
- }
103
-
104
- # Create a fake client which sends draft 76 handshake
105
- connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
106
- connection.send_data(format_request(@request))
107
-
108
- # Send closing frame after handshake complete, followed by another msg
109
- connection.onopen {
110
- connection.send_data(EM::WebSocket::Handler76::TERMINATE_STRING)
111
- connection.send('foobar')
92
+ EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
93
+ # Fail if foobar message is received
94
+ ws.onmessage { |msg|
95
+ fail
112
96
  }
113
-
114
- connection.onclose {
115
- done
116
- }
117
- end
97
+ }
98
+
99
+ # Create a fake client which sends draft 76 handshake
100
+ connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
101
+ connection.send_data(format_request(@request))
102
+
103
+ # Send closing frame after handshake complete, followed by another msg
104
+ connection.onopen {
105
+ connection.send_data(EM::WebSocket::Handler76::TERMINATE_STRING)
106
+ connection.send('foobar')
107
+ }
108
+
109
+ connection.onclose {
110
+ done
111
+ }
118
112
  }
119
113
  end
120
114
 
121
115
  it "should accept null bytes within the frame after a line return" do
122
116
  em {
123
- EM.add_timer(0.1) do
124
- EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
125
- ws.onmessage { |msg|
126
- msg.should == "\n\000"
127
- }
117
+ EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
118
+ ws.onmessage { |msg|
119
+ msg.should == "\n\000"
128
120
  }
129
-
130
- # Create a fake client which sends draft 76 handshake
131
- connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
132
- connection.send_data(format_request(@request))
133
-
134
- # Send closing frame after handshake complete
135
- connection.onopen {
136
- connection.send_data("\000\n\000\377")
137
- connection.send_data(EM::WebSocket::Handler76::TERMINATE_STRING)
138
- }
139
-
140
- connection.onclose {
141
- done
142
- }
143
- end
121
+ }
122
+
123
+ # Create a fake client which sends draft 76 handshake
124
+ connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
125
+ connection.send_data(format_request(@request))
126
+
127
+ # Send closing frame after handshake complete
128
+ connection.onopen {
129
+ connection.send_data("\000\n\000\377")
130
+ connection.send_data(EM::WebSocket::Handler76::TERMINATE_STRING)
131
+ }
132
+
133
+ connection.onclose {
134
+ done
135
+ }
144
136
  }
145
137
  end
146
138
 
@@ -205,25 +197,23 @@ describe "WebSocket server draft76" do
205
197
 
206
198
  it "should handle handshake request split into two TCP packets" do
207
199
  em {
208
- EM.add_timer(0.1) do
209
- EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { }
210
-
211
- # Create a fake client which sends draft 76 handshake
212
- connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
213
- data = format_request(@request)
214
- # Sends first half of the request
215
- connection.send_data(data[0...(data.length / 2)])
200
+ EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { }
216
201
 
217
- connection.onopen {
218
- connection.handshake_response.lines.sort.
219
- should == format_response(@response).lines.sort
220
- done
221
- }
202
+ # Create a fake client which sends draft 76 handshake
203
+ connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
204
+ data = format_request(@request)
205
+ # Sends first half of the request
206
+ connection.send_data(data[0...(data.length / 2)])
207
+
208
+ connection.onopen {
209
+ connection.handshake_response.lines.sort.
210
+ should == format_response(@response).lines.sort
211
+ done
212
+ }
222
213
 
223
- EM.add_timer(0.1) do
224
- # Sends second half of the request
225
- connection.send_data(data[(data.length / 2)..-1])
226
- end
214
+ EM.add_timer(0.1) do
215
+ # Sends second half of the request
216
+ connection.send_data(data[(data.length / 2)..-1])
227
217
  end
228
218
  }
229
219
  end
@@ -1,6 +1,11 @@
1
1
  require 'helper'
2
2
 
3
3
  describe "EventMachine::WebSocket::Handler" do
4
+ def handler(request, secure = false)
5
+ connection = Object.new
6
+ EM::WebSocket::HandlerFactory.build(connection, format_request(request), secure)
7
+ end
8
+
4
9
  before :each do
5
10
  @request = {
6
11
  :port => 80,
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: em-websocket
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.5
4
+ version: 0.3.6
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,11 +10,11 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2011-10-24 00:00:00.000000000Z
13
+ date: 2011-12-23 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: eventmachine
17
- requirement: &70358050200200 !ruby/object:Gem::Requirement
17
+ requirement: &70364656209880 !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ! '>='
@@ -22,10 +22,10 @@ dependencies:
22
22
  version: 0.12.9
23
23
  type: :runtime
24
24
  prerelease: false
25
- version_requirements: *70358050200200
25
+ version_requirements: *70364656209880
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: addressable
28
- requirement: &70358050199560 !ruby/object:Gem::Requirement
28
+ requirement: &70364656231520 !ruby/object:Gem::Requirement
29
29
  none: false
30
30
  requirements:
31
31
  - - ! '>='
@@ -33,10 +33,10 @@ dependencies:
33
33
  version: 2.1.1
34
34
  type: :runtime
35
35
  prerelease: false
36
- version_requirements: *70358050199560
36
+ version_requirements: *70364656231520
37
37
  - !ruby/object:Gem::Dependency
38
38
  name: em-spec
39
- requirement: &70358050199080 !ruby/object:Gem::Requirement
39
+ requirement: &70364656231060 !ruby/object:Gem::Requirement
40
40
  none: false
41
41
  requirements:
42
42
  - - ~>
@@ -44,10 +44,10 @@ dependencies:
44
44
  version: 0.2.5
45
45
  type: :development
46
46
  prerelease: false
47
- version_requirements: *70358050199080
47
+ version_requirements: *70364656231060
48
48
  - !ruby/object:Gem::Dependency
49
49
  name: eventmachine
50
- requirement: &70358050198540 !ruby/object:Gem::Requirement
50
+ requirement: &70364656230580 !ruby/object:Gem::Requirement
51
51
  none: false
52
52
  requirements:
53
53
  - - ~>
@@ -55,10 +55,10 @@ dependencies:
55
55
  version: 0.12.10
56
56
  type: :development
57
57
  prerelease: false
58
- version_requirements: *70358050198540
58
+ version_requirements: *70364656230580
59
59
  - !ruby/object:Gem::Dependency
60
60
  name: em-http-request
61
- requirement: &70358050197960 !ruby/object:Gem::Requirement
61
+ requirement: &70364656230100 !ruby/object:Gem::Requirement
62
62
  none: false
63
63
  requirements:
64
64
  - - ~>
@@ -66,10 +66,10 @@ dependencies:
66
66
  version: 0.2.6
67
67
  type: :development
68
68
  prerelease: false
69
- version_requirements: *70358050197960
69
+ version_requirements: *70364656230100
70
70
  - !ruby/object:Gem::Dependency
71
71
  name: rspec
72
- requirement: &70358050197280 !ruby/object:Gem::Requirement
72
+ requirement: &70364656229640 !ruby/object:Gem::Requirement
73
73
  none: false
74
74
  requirements:
75
75
  - - ~>
@@ -77,10 +77,10 @@ dependencies:
77
77
  version: 2.6.0
78
78
  type: :development
79
79
  prerelease: false
80
- version_requirements: *70358050197280
80
+ version_requirements: *70364656229640
81
81
  - !ruby/object:Gem::Dependency
82
82
  name: rake
83
- requirement: &70358050176420 !ruby/object:Gem::Requirement
83
+ requirement: &70364656229260 !ruby/object:Gem::Requirement
84
84
  none: false
85
85
  requirements:
86
86
  - - ! '>='
@@ -88,7 +88,7 @@ dependencies:
88
88
  version: '0'
89
89
  type: :development
90
90
  prerelease: false
91
- version_requirements: *70358050176420
91
+ version_requirements: *70364656229260
92
92
  description: EventMachine based WebSocket server
93
93
  email:
94
94
  - ilya@igvita.com
@@ -110,6 +110,7 @@ files:
110
110
  - examples/js/swfobject.js
111
111
  - examples/js/web_socket.js
112
112
  - examples/multicast.rb
113
+ - examples/ping.rb
113
114
  - examples/test.html
114
115
  - lib/em-websocket.rb
115
116
  - lib/em-websocket/close03.rb
@@ -173,7 +174,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
173
174
  version: '0'
174
175
  requirements: []
175
176
  rubyforge_project: em-websocket
176
- rubygems_version: 1.8.11
177
+ rubygems_version: 1.8.10
177
178
  signing_key:
178
179
  specification_version: 3
179
180
  summary: EventMachine based WebSocket server