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.
- data/CHANGELOG.rdoc +6 -0
- data/examples/echo.rb +2 -2
- data/examples/ping.rb +24 -0
- data/lib/em-websocket/connection.rb +45 -0
- data/lib/em-websocket/handler.rb +10 -0
- data/lib/em-websocket/message_processor_03.rb +7 -1
- data/lib/em-websocket/message_processor_06.rb +7 -1
- data/lib/em-websocket/version.rb +1 -1
- data/spec/helper.rb +0 -5
- data/spec/integration/draft13_spec.rb +40 -11
- data/spec/integration/draft76_spec.rb +81 -91
- data/spec/unit/handler_spec.rb +5 -0
- metadata +18 -17
data/CHANGELOG.rdoc
CHANGED
data/examples/echo.rb
CHANGED
@@ -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
|
data/examples/ping.rb
ADDED
@@ -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
|
data/lib/em-websocket/handler.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
data/lib/em-websocket/version.rb
CHANGED
data/spec/helper.rb
CHANGED
@@ -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
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
connection.
|
58
|
-
|
59
|
-
|
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
|
-
|
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
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
connection.
|
61
|
-
|
62
|
-
|
63
|
-
|
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
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
connection.
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
connection.
|
86
|
-
|
87
|
-
|
88
|
-
|
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
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
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
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
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
|
-
|
124
|
-
|
125
|
-
|
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
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
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
|
-
|
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
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
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
|
-
|
224
|
-
|
225
|
-
|
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
|
data/spec/unit/handler_spec.rb
CHANGED
@@ -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.
|
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-
|
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: &
|
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: *
|
25
|
+
version_requirements: *70364656209880
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: addressable
|
28
|
-
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: *
|
36
|
+
version_requirements: *70364656231520
|
37
37
|
- !ruby/object:Gem::Dependency
|
38
38
|
name: em-spec
|
39
|
-
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: *
|
47
|
+
version_requirements: *70364656231060
|
48
48
|
- !ruby/object:Gem::Dependency
|
49
49
|
name: eventmachine
|
50
|
-
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: *
|
58
|
+
version_requirements: *70364656230580
|
59
59
|
- !ruby/object:Gem::Dependency
|
60
60
|
name: em-http-request
|
61
|
-
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: *
|
69
|
+
version_requirements: *70364656230100
|
70
70
|
- !ruby/object:Gem::Dependency
|
71
71
|
name: rspec
|
72
|
-
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: *
|
80
|
+
version_requirements: *70364656229640
|
81
81
|
- !ruby/object:Gem::Dependency
|
82
82
|
name: rake
|
83
|
-
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: *
|
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.
|
177
|
+
rubygems_version: 1.8.10
|
177
178
|
signing_key:
|
178
179
|
specification_version: 3
|
179
180
|
summary: EventMachine based WebSocket server
|