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.
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
@@ -1,118 +1,138 @@
1
1
  require 'helper'
2
2
 
3
- # These tests are not specifi to any particular draft of the specification
4
- #
3
+ # These tests are not specific to any particular draft of the specification
4
+ #
5
5
  describe "WebSocket server" do
6
6
  include EM::SpecHelper
7
7
  default_timeout 1
8
8
 
9
9
  it "should fail on non WebSocket requests" do
10
10
  em {
11
- EventMachine.add_timer(0.1) do
12
- http = EventMachine::HttpRequest.new('http://127.0.0.1:12345/').get :timeout => 0
11
+ EM.add_timer(0.1) do
12
+ http = EM::HttpRequest.new('http://127.0.0.1:12345/').get :timeout => 0
13
13
  http.errback { done }
14
14
  http.callback { fail }
15
15
  end
16
16
 
17
- EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) {}
17
+ start_server
18
18
  }
19
19
  end
20
-
21
- it "should populate ws.request with appropriate headers" do
20
+
21
+ it "should expose the WebSocket request headers, path and query params" do
22
22
  em {
23
- EventMachine.add_timer(0.1) do
24
- http = EventMachine::HttpRequest.new('ws://127.0.0.1:12345/').get :timeout => 0
25
- http.errback { fail }
26
- http.callback {
27
- http.response_header.status.should == 101
28
- http.close_connection
29
- }
30
- http.stream { |msg| }
23
+ EM.add_timer(0.1) do
24
+ ws = EventMachine::WebSocketClient.connect('ws://127.0.0.1:12345/',
25
+ :origin => 'http://example.com')
26
+ ws.errback { fail }
27
+ ws.callback { ws.close_connection }
28
+ ws.stream { |msg| }
31
29
  end
32
30
 
33
- EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) do |ws|
34
- ws.onopen {
35
- ws.request["user-agent"].should == "EventMachine HttpClient"
36
- ws.request["connection"].should == "Upgrade"
37
- ws.request["upgrade"].should == "WebSocket"
38
- ws.request["path"].should == "/"
39
- ws.request["origin"].should == "127.0.0.1"
40
- ws.request["host"].to_s.should == "ws://127.0.0.1:12345"
31
+ start_server do |ws|
32
+ ws.onopen { |handshake|
33
+ headers = handshake.headers
34
+ headers["Connection"].should == "Upgrade"
35
+ headers["Upgrade"].should == "websocket"
36
+ headers["Host"].to_s.should == "127.0.0.1:12345"
37
+ handshake.path.should == "/"
38
+ handshake.query.should == {}
39
+ handshake.origin.should == 'http://example.com'
41
40
  }
42
41
  ws.onclose {
43
42
  ws.state.should == :closed
44
- EventMachine.stop
43
+ done
45
44
  }
46
45
  end
47
46
  }
48
47
  end
49
-
50
- it "should allow sending and retrieving query string args passed in on the connection request." do
48
+
49
+ it "should expose the WebSocket path and query params when nonempty" do
51
50
  em {
52
- EventMachine.add_timer(0.1) do
53
- http = EventMachine::HttpRequest.new('ws://127.0.0.1:12345/').get(:query => {'foo' => 'bar', 'baz' => 'qux'}, :timeout => 0)
54
- http.errback { fail }
55
- http.callback {
56
- http.response_header.status.should == 101
57
- http.close_connection
51
+ EM.add_timer(0.1) do
52
+ ws = EventMachine::WebSocketClient.connect('ws://127.0.0.1:12345/hello?foo=bar&baz=qux')
53
+ ws.errback { fail }
54
+ ws.callback {
55
+ ws.close_connection
58
56
  }
59
- http.stream { |msg| }
57
+ ws.stream { |msg| }
60
58
  end
61
59
 
62
- EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) do |ws|
63
- ws.onopen {
64
- path, query = ws.request["path"].split('?')
65
- path.should == '/'
66
- Hash[*query.split(/&|=/)].should == {"foo"=>"bar", "baz"=>"qux"}
67
- ws.request["query"]["foo"].should == "bar"
68
- ws.request["query"]["baz"].should == "qux"
60
+ start_server do |ws|
61
+ ws.onopen { |handshake|
62
+ handshake.path.should == '/hello'
63
+ handshake.query_string.split('&').sort.
64
+ should == ["baz=qux", "foo=bar"]
65
+ handshake.query.should == {"foo"=>"bar", "baz"=>"qux"}
69
66
  }
70
67
  ws.onclose {
71
68
  ws.state.should == :closed
72
- EventMachine.stop
69
+ done
73
70
  }
74
71
  end
75
72
  }
76
73
  end
77
-
78
- it "should ws.response['Query'] to empty hash when no query string params passed in connection URI" do
74
+
75
+ it "should raise an exception if frame sent before handshake complete" do
79
76
  em {
80
- EventMachine.add_timer(0.1) do
81
- http = EventMachine::HttpRequest.new('ws://127.0.0.1:12345/').get(:timeout => 0)
82
- http.errback { fail }
83
- http.callback {
84
- http.response_header.status.should == 101
85
- http.close_connection
86
- }
87
- http.stream { |msg| }
77
+ # 1. Start WebSocket server
78
+ start_server { |ws|
79
+ # 3. Try to send a message to the socket
80
+ lambda {
81
+ ws.send('early message')
82
+ }.should raise_error('Cannot send data before onopen callback')
83
+ done
84
+ }
85
+
86
+ # 2. Connect a dumb TCP connection (will not send handshake)
87
+ EM.connect('0.0.0.0', 12345, EM::Connection)
88
+ }
89
+ end
90
+
91
+ it "should allow the server to be started inside an existing EM" do
92
+ em {
93
+ EM.add_timer(0.1) do
94
+ http = EM::HttpRequest.new('http://127.0.0.1:12345/').get :timeout => 0
95
+ http.errback { |e| done }
96
+ http.callback { fail }
88
97
  end
89
98
 
90
- EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) do |ws|
91
- ws.onopen {
92
- ws.request["path"].should == "/"
93
- ws.request["query"].should == {}
99
+ start_server do |ws|
100
+ ws.onopen { |handshake|
101
+ headers = handshake.headers
102
+ headers["Host"].to_s.should == "127.0.0.1:12345"
94
103
  }
95
104
  ws.onclose {
96
105
  ws.state.should == :closed
97
- EventMachine.stop
106
+ done
98
107
  }
99
108
  end
100
109
  }
101
110
  end
102
-
103
- it "should raise an exception if frame sent before handshake complete" do
104
- em {
105
- EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |c|
106
- # We're not using a real client so the handshake will not be sent
107
- EM.add_timer(0.1) {
108
- lambda {
109
- c.send('early message')
110
- }.should raise_error('Cannot send data before onopen callback')
111
- done
112
- }
113
- }
114
111
 
115
- client = EM.connect('0.0.0.0', 12345, EM::Connection)
116
- }
112
+ context "outbound limit set" do
113
+ it "should close the connection if the limit is reached" do
114
+ em {
115
+ start_server(:outbound_limit => 150) do |ws|
116
+ # Increase the message size by one on each loop
117
+ ws.onmessage{|msg| ws.send(msg + "x") }
118
+ ws.onclose{|status|
119
+ status[:code].should == 1006 # Unclean
120
+ status[:was_clean].should be_false
121
+ }
122
+ end
123
+
124
+ EM.add_timer(0.1) do
125
+ ws = EventMachine::WebSocketClient.connect('ws://127.0.0.1:12345/')
126
+ ws.callback { ws.send_msg "hello" }
127
+ ws.disconnect { done } # Server closed the connection
128
+ ws.stream { |msg|
129
+ # minus frame size ? (getting 146 max here)
130
+ msg.data.size.should <= 150
131
+ # Return back the message
132
+ ws.send_msg(msg.data)
133
+ }
134
+ end
135
+ }
136
+ end
117
137
  end
118
138
  end
@@ -1,5 +1,4 @@
1
1
  require 'helper'
2
- require 'integration/shared_examples'
3
2
 
4
3
  describe "draft03" do
5
4
  include EM::SpecHelper
@@ -35,18 +34,19 @@ describe "draft03" do
35
34
  }
36
35
  end
37
36
 
37
+ def start_client
38
+ client = EM.connect('0.0.0.0', 12345, Draft03FakeWebSocketClient)
39
+ client.send_data(format_request(@request))
40
+ yield client if block_given?
41
+ return client
42
+ end
43
+
38
44
  it_behaves_like "a websocket server" do
39
- def start_server
40
- EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
41
- yield ws
42
- }
43
- end
45
+ let(:version) { 3 }
46
+ end
44
47
 
45
- def start_client
46
- client = EM.connect('0.0.0.0', 12345, Draft03FakeWebSocketClient)
47
- client.send_data(format_request(@request))
48
- yield client if block_given?
49
- end
48
+ it_behaves_like "a WebSocket server drafts 3 and above" do
49
+ let(:version) { 3 }
50
50
  end
51
51
 
52
52
  # These examples are straight from the spec
@@ -54,36 +54,30 @@ describe "draft03" do
54
54
  describe "examples from the spec" do
55
55
  it "should accept a single-frame text message" do
56
56
  em {
57
- EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
57
+ start_server { |ws|
58
58
  ws.onmessage { |msg|
59
59
  msg.should == 'Hello'
60
60
  done
61
61
  }
62
62
  }
63
-
64
- # Create a fake client which sends draft 76 handshake
65
- connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
66
- connection.send_data(format_request(@request))
67
-
68
- # Send frame
69
- connection.onopen {
70
- connection.send_data("\x04\x05Hello")
63
+ start_client { |client|
64
+ client.onopen {
65
+ client.send_data("\x04\x05Hello")
66
+ }
71
67
  }
72
68
  }
73
69
  end
74
70
 
75
71
  it "should accept a fragmented text message" do
76
72
  em {
77
- EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
73
+ start_server { |ws|
78
74
  ws.onmessage { |msg|
79
75
  msg.should == 'Hello'
80
76
  done
81
77
  }
82
78
  }
83
79
 
84
- # Create a fake client which sends draft 76 handshake
85
- connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
86
- connection.send_data(format_request(@request))
80
+ connection = start_client
87
81
 
88
82
  # Send frame
89
83
  connection.onopen {
@@ -95,11 +89,9 @@ describe "draft03" do
95
89
 
96
90
  it "should accept a ping request and respond with the same body" do
97
91
  em {
98
- EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws| }
92
+ start_server
99
93
 
100
- # Create a fake client which sends draft 76 handshake
101
- connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
102
- connection.send_data(format_request(@request))
94
+ connection = start_client
103
95
 
104
96
  # Send frame
105
97
  connection.onopen {
@@ -118,16 +110,15 @@ describe "draft03" do
118
110
  em {
119
111
  data = "a" * 256
120
112
 
121
- EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
122
- ws.onmessage { |msg|
113
+ start_server { |ws|
114
+ ws.onbinary { |msg|
115
+ msg.encoding.should == Encoding.find("BINARY") if defined?(Encoding)
123
116
  msg.should == data
124
117
  done
125
118
  }
126
119
  }
127
120
 
128
- # Create a fake client which sends draft 76 handshake
129
- connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
130
- connection.send_data(format_request(@request))
121
+ connection = start_client
131
122
 
132
123
  # Send frame
133
124
  connection.onopen {
@@ -140,16 +131,15 @@ describe "draft03" do
140
131
  em {
141
132
  data = "a" * 65536
142
133
 
143
- EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
144
- ws.onmessage { |msg|
134
+ start_server { |ws|
135
+ ws.onbinary { |msg|
136
+ msg.encoding.should == Encoding.find("BINARY") if defined?(Encoding)
145
137
  msg.should == data
146
138
  done
147
139
  }
148
140
  }
149
141
 
150
- # Create a fake client which sends draft 76 handshake
151
- connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
152
- connection.send_data(format_request(@request))
142
+ connection = start_client
153
143
 
154
144
  # Send frame
155
145
  connection.onopen {
@@ -162,11 +152,9 @@ describe "draft03" do
162
152
  describe "close handling" do
163
153
  it "should respond to a new close frame with a close frame" do
164
154
  em {
165
- EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws| }
155
+ start_server
166
156
 
167
- # Create a fake client which sends draft 76 handshake
168
- connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
169
- connection.send_data(format_request(@request))
157
+ connection = start_client
170
158
 
171
159
  # Send close frame
172
160
  connection.onopen {
@@ -181,22 +169,31 @@ describe "draft03" do
181
169
  }
182
170
  end
183
171
 
184
- it "should close the connection on receiving a close acknowlegement" do
172
+ it "should close the connection on receiving a close acknowlegement and call onclose with close code 1005 and was_clean=true (initiated by server)" do
185
173
  em {
186
174
  ack_received = false
187
175
 
188
- EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
176
+ start_server { |ws|
189
177
  ws.onopen {
190
178
  # 2. Send a close frame
191
179
  EM.next_tick {
192
- ws.close_websocket
180
+ ws.close
193
181
  }
194
182
  }
183
+
184
+ # 5. Onclose event on server
185
+ ws.onclose { |event|
186
+ event.should == {
187
+ :code => 1005,
188
+ :reason => "",
189
+ :was_clean => true,
190
+ }
191
+ done
192
+ }
195
193
  }
196
194
 
197
195
  # 1. Create a fake client which sends draft 76 handshake
198
- connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
199
- connection.send_data(format_request(@request))
196
+ connection = start_client
200
197
 
201
198
  # 3. Check that close frame recieved and acknowlege it
202
199
  connection.onmessage { |frame|
@@ -208,18 +205,39 @@ describe "draft03" do
208
205
  # 4. Check that connection is closed _after_ the ack
209
206
  connection.onclose {
210
207
  ack_received.should == true
211
- done
208
+ }
209
+ }
210
+ end
211
+
212
+ # it "should repur"
213
+ #
214
+ it "should return close code 1005 and was_clean=true after closing handshake (initiated by client)" do
215
+ em {
216
+ start_server { |ws|
217
+ ws.onclose { |event|
218
+ event.should == {
219
+ :code => 1005,
220
+ :reason => "",
221
+ :was_clean => true,
222
+ }
223
+ done
224
+ }
225
+ }
226
+ start_client { |client|
227
+ client.onopen {
228
+ client.send_data("\x01\x00")
229
+ }
212
230
  }
213
231
  }
214
232
  end
215
233
 
216
234
  it "should not allow data frame to be sent after close frame sent" do
217
235
  em {
218
- EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
236
+ start_server { |ws|
219
237
  ws.onopen {
220
238
  # 2. Send a close frame
221
239
  EM.next_tick {
222
- ws.close_websocket
240
+ ws.close
223
241
  }
224
242
 
225
243
  # 3. Check that exception raised if I attempt to send more data
@@ -233,25 +251,23 @@ describe "draft03" do
233
251
  }
234
252
 
235
253
  # 1. Create a fake client which sends draft 76 handshake
236
- connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
237
- connection.send_data(format_request(@request))
254
+ start_client
238
255
  }
239
256
  end
240
257
 
241
258
  it "should still respond to control frames after close frame sent" do
242
259
  em {
243
- EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
260
+ start_server { |ws|
244
261
  ws.onopen {
245
262
  # 2. Send a close frame
246
263
  EM.next_tick {
247
- ws.close_websocket
264
+ ws.close
248
265
  }
249
266
  }
250
267
  }
251
268
 
252
269
  # 1. Create a fake client which sends draft 76 handshake
253
- connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
254
- connection.send_data(format_request(@request))
270
+ connection = start_client
255
271
 
256
272
  connection.onmessage { |frame|
257
273
  if frame == "\x01\x00"
@@ -266,5 +282,17 @@ describe "draft03" do
266
282
  }
267
283
  }
268
284
  end
285
+
286
+ it "should report that close codes are not supported" do
287
+ em {
288
+ start_server { |ws|
289
+ ws.onopen {
290
+ ws.supports_close_codes?.should == false
291
+ done
292
+ }
293
+ }
294
+ start_client
295
+ }
296
+ end
269
297
  end
270
298
  end