sonixlabs-em-websocket 0.3.8 → 0.5.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.rdoc +69 -0
  3. data/Gemfile +6 -0
  4. data/LICENCE +7 -0
  5. data/README.md +100 -56
  6. data/README.md.BACKUP.14928.md +195 -0
  7. data/README.md.BASE.14928.md +77 -0
  8. data/README.md.LOCAL.14928.md +98 -0
  9. data/README.md.REMOTE.14928.md +142 -0
  10. data/examples/echo.rb +23 -7
  11. data/examples/ping.rb +24 -0
  12. data/examples/test.html +5 -6
  13. data/lib/em-websocket.rb +4 -2
  14. data/lib/em-websocket/close03.rb +3 -0
  15. data/lib/em-websocket/close05.rb +3 -0
  16. data/lib/em-websocket/close06.rb +3 -0
  17. data/lib/em-websocket/close75.rb +2 -1
  18. data/lib/em-websocket/connection.rb +219 -73
  19. data/lib/em-websocket/framing03.rb +6 -11
  20. data/lib/em-websocket/framing05.rb +6 -11
  21. data/lib/em-websocket/framing07.rb +25 -20
  22. data/lib/em-websocket/framing76.rb +6 -15
  23. data/lib/em-websocket/handler.rb +69 -28
  24. data/lib/em-websocket/handler03.rb +0 -1
  25. data/lib/em-websocket/handler05.rb +0 -1
  26. data/lib/em-websocket/handler06.rb +0 -1
  27. data/lib/em-websocket/handler07.rb +0 -1
  28. data/lib/em-websocket/handler08.rb +0 -1
  29. data/lib/em-websocket/handler13.rb +0 -1
  30. data/lib/em-websocket/handler76.rb +2 -0
  31. data/lib/em-websocket/handshake.rb +156 -0
  32. data/lib/em-websocket/handshake04.rb +18 -56
  33. data/lib/em-websocket/handshake75.rb +15 -8
  34. data/lib/em-websocket/handshake76.rb +15 -14
  35. data/lib/em-websocket/masking04.rb +4 -30
  36. data/lib/em-websocket/message_processor_03.rb +13 -4
  37. data/lib/em-websocket/message_processor_06.rb +25 -13
  38. data/lib/em-websocket/version.rb +1 -1
  39. data/lib/em-websocket/websocket.rb +35 -24
  40. data/spec/helper.rb +82 -55
  41. data/spec/integration/common_spec.rb +90 -70
  42. data/spec/integration/draft03_spec.rb +84 -56
  43. data/spec/integration/draft05_spec.rb +14 -12
  44. data/spec/integration/draft06_spec.rb +66 -9
  45. data/spec/integration/draft13_spec.rb +59 -29
  46. data/spec/integration/draft75_spec.rb +46 -40
  47. data/spec/integration/draft76_spec.rb +113 -109
  48. data/spec/integration/gte_03_examples.rb +42 -0
  49. data/spec/integration/shared_examples.rb +174 -0
  50. data/spec/unit/framing_spec.rb +83 -110
  51. data/spec/unit/handshake_spec.rb +216 -0
  52. data/spec/unit/masking_spec.rb +2 -0
  53. metadata +31 -71
  54. data/examples/flash_policy_file_server.rb +0 -21
  55. data/examples/js/FABridge.js +0 -604
  56. data/examples/js/WebSocketMain.swf +0 -0
  57. data/examples/js/swfobject.js +0 -4
  58. data/examples/js/web_socket.js +0 -312
  59. data/lib/em-websocket/handler_factory.rb +0 -107
  60. 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
@@ -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(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(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(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
- # NOTE I modified these to be compliant with the rule that client data must be masked
203
- describe "server side" do
204
- describe "examples from the spec" do
205
- it "rejects a single-frame unmasked text message from the client" do
206
- lambda {
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
- it "Ping request" do
223
- @f.should_receive(:message).with(:ping, '', 'Hello')
224
- @f << "\x89\x85\x01\x01\x01\x01Idmmn"
225
- end
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
- it "a pong response" do
228
- @f.should_receive(:message).with(:pong, '', 'Hello')
229
- @f << "\x8a\x85\x01\x01\x01\x01Idmmn"
230
- end
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
- it "256 bytes binary message in a single masked frame" do
233
- data = "b"*256
234
- masked_data = "c"*256
235
- @f.should_receive(:message).with(:binary, '', data)
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
- it "64KiB binary message in a single masked frame" do
240
- data = "b"*65536
241
- masked_data = "c"*65536
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
- describe "other tests" do
248
- it "should raise a DataError if an invalid frame type is requested" do
249
- lambda {
250
- # Opcode 3 is not supported by this draft
251
- @f << "\x83\x85\x01\x01\x01\x01Idmmn"
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
- it "should accept a fragmented masked text message in 3 frames" do
256
- @f.should_receive(:message).with(:text, '', 'Hello world')
257
- @f << "\x01\x83\x01\x01\x01\x01Idm"
258
- @f << "\x00\x82\x01\x01\x01\x01mn"
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 "client side" do
265
- before do
266
- @f.mask_outbound_messages = true
267
- @f.require_masked_inbound_messages = false
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
- it "a pong response" do
292
- @f.should_receive(:message).with(:pong, '', 'Hello')
293
- @f << "\x8a\x05Hello"
294
- end
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
- describe "other tests" do
310
- it "should raise a DataError if an invalid frame type is requested" do
311
- lambda {
312
- # Opcode 3 is not supported by this draft
313
- @f << "\x83\x05Hello"
314
- }.should raise_error(EventMachine::WebSocket::DataError, "Unknown opcode")
315
- end
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
- it "should accept a fragmented unmasked text message in 3 frames" do
318
- @f.should_receive(:message).with(:text, '', 'Hello world')
319
- @f << "\x01\x03Hel"
320
- @f << "\x00\x02lo"
321
- @f << "\x80\x06 world"
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