sonixlabs-em-websocket 0.3.8 → 0.5.1.1
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.
- checksums.yaml +7 -0
- data/CHANGELOG.rdoc +69 -0
- data/Gemfile +6 -0
- data/LICENCE +7 -0
- data/README.md +100 -56
- data/README.md.BACKUP.14928.md +195 -0
- data/README.md.BASE.14928.md +77 -0
- data/README.md.LOCAL.14928.md +98 -0
- data/README.md.REMOTE.14928.md +142 -0
- data/examples/echo.rb +23 -7
- data/examples/ping.rb +24 -0
- data/examples/test.html +5 -6
- data/lib/em-websocket.rb +4 -2
- data/lib/em-websocket/close03.rb +3 -0
- data/lib/em-websocket/close05.rb +3 -0
- data/lib/em-websocket/close06.rb +3 -0
- data/lib/em-websocket/close75.rb +2 -1
- data/lib/em-websocket/connection.rb +219 -73
- data/lib/em-websocket/framing03.rb +6 -11
- data/lib/em-websocket/framing05.rb +6 -11
- data/lib/em-websocket/framing07.rb +25 -20
- data/lib/em-websocket/framing76.rb +6 -15
- data/lib/em-websocket/handler.rb +69 -28
- data/lib/em-websocket/handler03.rb +0 -1
- data/lib/em-websocket/handler05.rb +0 -1
- data/lib/em-websocket/handler06.rb +0 -1
- data/lib/em-websocket/handler07.rb +0 -1
- data/lib/em-websocket/handler08.rb +0 -1
- data/lib/em-websocket/handler13.rb +0 -1
- data/lib/em-websocket/handler76.rb +2 -0
- data/lib/em-websocket/handshake.rb +156 -0
- data/lib/em-websocket/handshake04.rb +18 -56
- data/lib/em-websocket/handshake75.rb +15 -8
- data/lib/em-websocket/handshake76.rb +15 -14
- data/lib/em-websocket/masking04.rb +4 -30
- data/lib/em-websocket/message_processor_03.rb +13 -4
- data/lib/em-websocket/message_processor_06.rb +25 -13
- data/lib/em-websocket/version.rb +1 -1
- data/lib/em-websocket/websocket.rb +35 -24
- data/spec/helper.rb +82 -55
- data/spec/integration/common_spec.rb +90 -70
- data/spec/integration/draft03_spec.rb +84 -56
- data/spec/integration/draft05_spec.rb +14 -12
- data/spec/integration/draft06_spec.rb +66 -9
- data/spec/integration/draft13_spec.rb +59 -29
- data/spec/integration/draft75_spec.rb +46 -40
- data/spec/integration/draft76_spec.rb +113 -109
- data/spec/integration/gte_03_examples.rb +42 -0
- data/spec/integration/shared_examples.rb +174 -0
- data/spec/unit/framing_spec.rb +83 -110
- data/spec/unit/handshake_spec.rb +216 -0
- data/spec/unit/masking_spec.rb +2 -0
- metadata +31 -71
- data/examples/flash_policy_file_server.rb +0 -21
- data/examples/js/FABridge.js +0 -604
- data/examples/js/WebSocketMain.swf +0 -0
- data/examples/js/swfobject.js +0 -4
- data/examples/js/web_socket.js +0 -312
- data/lib/em-websocket/handler_factory.rb +0 -107
- data/spec/unit/handler_spec.rb +0 -147
@@ -0,0 +1,42 @@
|
|
1
|
+
shared_examples_for "a WebSocket server drafts 3 and above" do
|
2
|
+
it "should force close connections after a timeout if close handshake is not sent by the client" do
|
3
|
+
em {
|
4
|
+
server_onerror_fired = false
|
5
|
+
server_onclose_fired = false
|
6
|
+
client_got_close_handshake = false
|
7
|
+
|
8
|
+
start_server(:close_timeout => 0.1) { |ws|
|
9
|
+
ws.onopen {
|
10
|
+
# 1: Send close handshake to client
|
11
|
+
EM.next_tick { ws.close(4999, "Close message") }
|
12
|
+
}
|
13
|
+
|
14
|
+
ws.onerror { |e|
|
15
|
+
# 3: Client should receive onerror
|
16
|
+
e.class.should == EM::WebSocket::WSProtocolError
|
17
|
+
e.message.should == "Close handshake un-acked after 0.1s, closing tcp connection"
|
18
|
+
server_onerror_fired = true
|
19
|
+
}
|
20
|
+
|
21
|
+
ws.onclose {
|
22
|
+
server_onclose_fired = true
|
23
|
+
}
|
24
|
+
}
|
25
|
+
start_client { |client|
|
26
|
+
client.onmessage { |msg|
|
27
|
+
# 2: Client does not respond to close handshake (the fake client
|
28
|
+
# doesn't understand them at all hence this is in onmessage)
|
29
|
+
msg.should =~ /Close message/ if version >= 6
|
30
|
+
client_got_close_handshake = true
|
31
|
+
}
|
32
|
+
|
33
|
+
client.onclose {
|
34
|
+
server_onerror_fired.should == true
|
35
|
+
server_onclose_fired.should == true
|
36
|
+
client_got_close_handshake.should == true
|
37
|
+
done
|
38
|
+
}
|
39
|
+
}
|
40
|
+
}
|
41
|
+
end
|
42
|
+
end
|
@@ -1,6 +1,127 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
|
+
# These tests are run against all draft versions
|
4
|
+
#
|
3
5
|
shared_examples_for "a websocket server" do
|
6
|
+
it "should expose the protocol version" do
|
7
|
+
em {
|
8
|
+
start_server { |ws|
|
9
|
+
ws.onopen { |handshake|
|
10
|
+
handshake.protocol_version.should == version
|
11
|
+
done
|
12
|
+
}
|
13
|
+
}
|
14
|
+
|
15
|
+
start_client
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should expose the origin header" do
|
20
|
+
em {
|
21
|
+
start_server { |ws|
|
22
|
+
ws.onopen { |handshake|
|
23
|
+
handshake.origin.should == 'http://example.com'
|
24
|
+
done
|
25
|
+
}
|
26
|
+
}
|
27
|
+
|
28
|
+
start_client
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should expose the remote IP address" do
|
33
|
+
em {
|
34
|
+
start_server { |ws|
|
35
|
+
ws.onopen {
|
36
|
+
ws.remote_ip.should == "127.0.0.1"
|
37
|
+
done
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
start_client
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should send messages successfully" do
|
46
|
+
em {
|
47
|
+
start_server { |ws|
|
48
|
+
ws.onmessage { |message|
|
49
|
+
message.should == "hello server"
|
50
|
+
done
|
51
|
+
}
|
52
|
+
}
|
53
|
+
|
54
|
+
start_client { |client|
|
55
|
+
client.onopen {
|
56
|
+
client.send("hello server")
|
57
|
+
}
|
58
|
+
}
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should allow connection to be closed with valid close code" do
|
63
|
+
em {
|
64
|
+
start_server { |ws|
|
65
|
+
ws.onopen {
|
66
|
+
ws.close(4004, "Bye bye")
|
67
|
+
done
|
68
|
+
}
|
69
|
+
}
|
70
|
+
|
71
|
+
start_client
|
72
|
+
# TODO: Use a real client which understands how to respond to closing
|
73
|
+
# handshakes, sending the handshake currently untested
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should raise error if if invalid close code is used" do
|
78
|
+
em {
|
79
|
+
start_server { |ws|
|
80
|
+
ws.onopen {
|
81
|
+
lambda {
|
82
|
+
ws.close(2000)
|
83
|
+
}.should raise_error("Application code may only use codes from 1000, 3000-4999")
|
84
|
+
done
|
85
|
+
}
|
86
|
+
}
|
87
|
+
|
88
|
+
start_client
|
89
|
+
}
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should call onclose with was_clean set to false if connection closed without closing handshake by server" do
|
93
|
+
em {
|
94
|
+
start_server { |ws|
|
95
|
+
ws.onopen {
|
96
|
+
# Close tcp connection (no close handshake)
|
97
|
+
ws.close_connection
|
98
|
+
}
|
99
|
+
ws.onclose { |event|
|
100
|
+
event.should == {:code => 1006, :was_clean => false}
|
101
|
+
done
|
102
|
+
}
|
103
|
+
}
|
104
|
+
start_client
|
105
|
+
}
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should call onclose with was_clean set to false if connection closed without closing handshake by client" do
|
109
|
+
em {
|
110
|
+
start_server { |ws|
|
111
|
+
ws.onclose { |event|
|
112
|
+
event.should == {:code => 1006, :was_clean => false}
|
113
|
+
done
|
114
|
+
}
|
115
|
+
}
|
116
|
+
start_client { |client|
|
117
|
+
client.onopen {
|
118
|
+
# Close tcp connection (no close handshake)
|
119
|
+
client.close_connection
|
120
|
+
}
|
121
|
+
}
|
122
|
+
}
|
123
|
+
end
|
124
|
+
|
4
125
|
it "should call onerror if an application error raised in onopen" do
|
5
126
|
em {
|
6
127
|
start_server { |ws|
|
@@ -62,6 +183,41 @@ shared_examples_for "a websocket server" do
|
|
62
183
|
}
|
63
184
|
end
|
64
185
|
|
186
|
+
it "should close the connection when a too long frame is sent" do
|
187
|
+
em {
|
188
|
+
start_server { |server|
|
189
|
+
server.max_frame_size = 20
|
190
|
+
|
191
|
+
server.onerror { |e|
|
192
|
+
# 3: Error should be reported to server
|
193
|
+
e.class.should == EventMachine::WebSocket::WSMessageTooBigError
|
194
|
+
e.message.should =~ /Frame length too long/
|
195
|
+
}
|
196
|
+
}
|
197
|
+
|
198
|
+
start_client { |client|
|
199
|
+
client.onopen {
|
200
|
+
EM.next_tick {
|
201
|
+
client.send("This message is longer than 20 characters")
|
202
|
+
}
|
203
|
+
|
204
|
+
}
|
205
|
+
|
206
|
+
client.onmessage { |msg|
|
207
|
+
# 4: This is actually the close message. Really need to use a real
|
208
|
+
# WebSocket client in these tests...
|
209
|
+
done
|
210
|
+
}
|
211
|
+
|
212
|
+
client.onclose {
|
213
|
+
# 4: Drafts 75 & 76 don't send a close message, they just close the
|
214
|
+
# connection
|
215
|
+
done
|
216
|
+
}
|
217
|
+
}
|
218
|
+
}
|
219
|
+
end
|
220
|
+
|
65
221
|
# Only run these tests on ruby 1.9
|
66
222
|
if "a".respond_to?(:force_encoding)
|
67
223
|
it "should raise error if you try to send non utf8 text data to ws" do
|
@@ -87,5 +243,23 @@ shared_examples_for "a websocket server" do
|
|
87
243
|
start_client { }
|
88
244
|
}
|
89
245
|
end
|
246
|
+
|
247
|
+
it "should not change the encoding of strings sent to send [antiregression]" do
|
248
|
+
em {
|
249
|
+
start_server { |server|
|
250
|
+
server.onopen {
|
251
|
+
s = "example string"
|
252
|
+
s.force_encoding("UTF-8")
|
253
|
+
|
254
|
+
server.send(s)
|
255
|
+
|
256
|
+
s.encoding.should == Encoding.find("UTF-8")
|
257
|
+
done
|
258
|
+
}
|
259
|
+
}
|
260
|
+
|
261
|
+
start_client { }
|
262
|
+
}
|
263
|
+
end
|
90
264
|
end
|
91
265
|
end
|
data/spec/unit/framing_spec.rb
CHANGED
@@ -1,14 +1,23 @@
|
|
1
|
+
# encoding: BINARY
|
2
|
+
|
1
3
|
require 'helper'
|
2
4
|
|
3
5
|
describe EM::WebSocket::Framing03 do
|
4
6
|
class FramingContainer
|
5
7
|
include EM::WebSocket::Framing03
|
6
8
|
|
9
|
+
def initialize
|
10
|
+
@connection = Object.new
|
11
|
+
def @connection.max_frame_size
|
12
|
+
1000000
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
7
16
|
def <<(data)
|
8
17
|
@data << data
|
9
|
-
process_data
|
18
|
+
process_data
|
10
19
|
end
|
11
|
-
|
20
|
+
|
12
21
|
def debug(*args); end
|
13
22
|
end
|
14
23
|
|
@@ -122,9 +131,16 @@ describe EM::WebSocket::Framing04 do
|
|
122
131
|
class FramingContainer04
|
123
132
|
include EM::WebSocket::Framing04
|
124
133
|
|
134
|
+
def initialize
|
135
|
+
@connection = Object.new
|
136
|
+
def @connection.max_frame_size
|
137
|
+
1000000
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
125
141
|
def <<(data)
|
126
142
|
@data << data
|
127
|
-
process_data
|
143
|
+
process_data
|
128
144
|
end
|
129
145
|
|
130
146
|
def debug(*args); end
|
@@ -184,9 +200,16 @@ describe EM::WebSocket::Framing07 do
|
|
184
200
|
class FramingContainer07
|
185
201
|
include EM::WebSocket::Framing07
|
186
202
|
|
203
|
+
def initialize
|
204
|
+
@connection = Object.new
|
205
|
+
def @connection.max_frame_size
|
206
|
+
1000000
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
187
210
|
def <<(data)
|
188
211
|
@data << data
|
189
|
-
process_data
|
212
|
+
process_data
|
190
213
|
end
|
191
214
|
|
192
215
|
def debug(*args); end
|
@@ -199,127 +222,77 @@ describe EM::WebSocket::Framing07 do
|
|
199
222
|
|
200
223
|
# These examples are straight from the spec
|
201
224
|
# http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-07#section-4.6
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
@f << "\x81\x05\x48\x65\x6c\x6c\x6f" # "\x84\x05Hello"
|
208
|
-
}.should raise_error(EventMachine::WebSocket::WebSocketError, 'Data from client must be masked')
|
209
|
-
end
|
210
|
-
|
211
|
-
it "a single-frame masked text message" do
|
212
|
-
@f.should_receive(:message).with(:text, '', 'Hello')
|
213
|
-
@f << "\x81\x85\x37\xfa\x21\x3d\x7f\x9f\x4d\x51\x58" # "\x84\x05Hello"
|
214
|
-
end
|
215
|
-
|
216
|
-
it "a fragmented masked text message" do
|
217
|
-
@f.should_receive(:message).with(:text, '', 'Hello')
|
218
|
-
@f << "\x01\x83\x01\x01\x01\x01Idm" # with mask x01x01x01x01, Hello -> Idmmn
|
219
|
-
@f << "\x80\x82\x01\x01\x01\x01mn"
|
220
|
-
end
|
225
|
+
describe "examples from the spec" do
|
226
|
+
it "a single-frame unmakedtext message" do
|
227
|
+
@f.should_receive(:message).with(:text, '', 'Hello')
|
228
|
+
@f << "\x81\x05\x48\x65\x6c\x6c\x6f" # "\x84\x05Hello"
|
229
|
+
end
|
221
230
|
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
231
|
+
it "a single-frame masked text message" do
|
232
|
+
@f.should_receive(:message).with(:text, '', 'Hello')
|
233
|
+
@f << "\x81\x85\x37\xfa\x21\x3d\x7f\x9f\x4d\x51\x58" # "\x84\x05Hello"
|
234
|
+
end
|
226
235
|
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
236
|
+
it "a fragmented unmasked text message" do
|
237
|
+
@f.should_receive(:message).with(:text, '', 'Hello')
|
238
|
+
@f << "\x01\x03Hel"
|
239
|
+
@f << "\x80\x02lo"
|
240
|
+
end
|
231
241
|
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
@f << "\x82\xFE\x01\x00\x01\x01\x01\x01" + masked_data
|
237
|
-
end
|
242
|
+
it "Ping request" do
|
243
|
+
@f.should_receive(:message).with(:ping, '', 'Hello')
|
244
|
+
@f << "\x89\x05Hello"
|
245
|
+
end
|
238
246
|
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
@f.should_receive(:message).with(:binary, '', data)
|
243
|
-
@f << "\x82\xFF\x00\x00\x00\x00\x00\x01\x00\x00\x01\x01\x01\x01" + masked_data
|
244
|
-
end
|
247
|
+
it "a pong response" do
|
248
|
+
@f.should_receive(:message).with(:pong, '', 'Hello')
|
249
|
+
@f << "\x8a\x05Hello"
|
245
250
|
end
|
246
251
|
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
}.should raise_error(EventMachine::WebSocket::DataError, "Unknown opcode")
|
253
|
-
end
|
252
|
+
it "256 bytes binary message in a single unmasked frame" do
|
253
|
+
data = "a"*256
|
254
|
+
@f.should_receive(:message).with(:binary, '', data)
|
255
|
+
@f << "\x82\x7E\x01\x00" + data
|
256
|
+
end
|
254
257
|
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
@f << "\x80\x86\x01\x01\x01\x01\x21vnsme"
|
260
|
-
end
|
258
|
+
it "64KiB binary message in a single unmasked frame" do
|
259
|
+
data = "a"*65536
|
260
|
+
@f.should_receive(:message).with(:binary, '', data)
|
261
|
+
@f << "\x82\x7F\x00\x00\x00\x00\x00\x01\x00\x00" + data
|
261
262
|
end
|
262
263
|
end
|
263
264
|
|
264
|
-
describe "
|
265
|
-
|
266
|
-
|
267
|
-
|
265
|
+
describe "other tests" do
|
266
|
+
it "should raise a WSProtocolError if an invalid frame type is requested" do
|
267
|
+
lambda {
|
268
|
+
# Opcode 3 is not supported by this draft
|
269
|
+
@f << "\x83\x05Hello"
|
270
|
+
}.should raise_error(EventMachine::WebSocket::WSProtocolError, "Unknown opcode 3")
|
268
271
|
end
|
269
|
-
describe "examples from the spec" do
|
270
|
-
it "accepts a single-frame unmasked text message from the client" do
|
271
|
-
@f.should_receive(:message).with(:text, '', 'Hello')
|
272
|
-
@f << "\x81\x05\x48\x65\x6c\x6c\x6f" # "\x84\x05Hello"
|
273
|
-
end
|
274
|
-
|
275
|
-
it "a single-frame masked text message" do
|
276
|
-
@f.should_receive(:message).with(:text, '', 'Hello')
|
277
|
-
@f << "\x81\x85\x37\xfa\x21\x3d\x7f\x9f\x4d\x51\x58" # "\x84\x05Hello"
|
278
|
-
end
|
279
|
-
|
280
|
-
it "a fragmented unmasked text message" do
|
281
|
-
@f.should_receive(:message).with(:text, '', 'Hello')
|
282
|
-
@f << "\x01\x03\Hel"
|
283
|
-
@f << "\x80\x02\lo"
|
284
|
-
end
|
285
|
-
|
286
|
-
it "Ping request" do
|
287
|
-
@f.should_receive(:message).with(:ping, '', 'Hello')
|
288
|
-
@f << "\x89\x05Hello"
|
289
|
-
end
|
290
272
|
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
it "256 bytes binary message in a single unmasked frame" do
|
297
|
-
data = "a"*256
|
298
|
-
@f.should_receive(:message).with(:binary, '', data)
|
299
|
-
@f << "\x82\x7E\x01\x00" + data
|
300
|
-
end
|
301
|
-
|
302
|
-
it "64KiB binary message in a single unmasked frame" do
|
303
|
-
data = "a"*65536
|
304
|
-
@f.should_receive(:message).with(:binary, '', data)
|
305
|
-
@f << "\x82\x7F\x00\x00\x00\x00\x00\x01\x00\x00" + data
|
306
|
-
end
|
273
|
+
it "should accept a fragmented unmasked text message in 3 frames" do
|
274
|
+
@f.should_receive(:message).with(:text, '', 'Hello world')
|
275
|
+
@f << "\x01\x03Hel"
|
276
|
+
@f << "\x00\x02lo"
|
277
|
+
@f << "\x80\x06 world"
|
307
278
|
end
|
308
279
|
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
280
|
+
it "should raise if non-fin frame is followed by a non-continuation data frame (continuation frame would be expected)" do
|
281
|
+
lambda {
|
282
|
+
@f << 0b00000001 # Not fin, text
|
283
|
+
@f << 0b00000001 # Length 1
|
284
|
+
@f << 'f'
|
285
|
+
@f << 0b10000001 # fin, text (continutation expected)
|
286
|
+
@f << 0b00000001 # Length 1
|
287
|
+
@f << 'b'
|
288
|
+
}.should raise_error(EM::WebSocket::WebSocketError, 'Continuation frame expected')
|
289
|
+
end
|
316
290
|
|
317
|
-
|
318
|
-
|
319
|
-
@f <<
|
320
|
-
@f <<
|
321
|
-
|
322
|
-
end
|
291
|
+
it "should raise on non-fin control frames (control frames must not be fragmented)" do
|
292
|
+
lambda {
|
293
|
+
@f << 0b00001010 # Not fin, pong (opcode 10)
|
294
|
+
@f << 0b00000000 # Length 1
|
295
|
+
}.should raise_error(EM::WebSocket::WebSocketError, 'Control frames must not be fragmented')
|
323
296
|
end
|
324
297
|
end
|
325
298
|
end
|