em-websocket 0.3.5 → 0.3.6

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