em-websocket 0.3.7 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.rdoc +52 -0
  3. data/Gemfile +6 -0
  4. data/LICENCE +7 -0
  5. data/README.md +105 -40
  6. data/examples/echo.rb +22 -6
  7. data/examples/test.html +5 -6
  8. data/lib/em-websocket.rb +2 -1
  9. data/lib/em-websocket/close03.rb +3 -0
  10. data/lib/em-websocket/close05.rb +3 -0
  11. data/lib/em-websocket/close06.rb +3 -0
  12. data/lib/em-websocket/close75.rb +2 -1
  13. data/lib/em-websocket/connection.rb +154 -48
  14. data/lib/em-websocket/framing03.rb +3 -2
  15. data/lib/em-websocket/framing05.rb +3 -2
  16. data/lib/em-websocket/framing07.rb +16 -4
  17. data/lib/em-websocket/framing76.rb +1 -4
  18. data/lib/em-websocket/handler.rb +61 -15
  19. data/lib/em-websocket/handler03.rb +0 -1
  20. data/lib/em-websocket/handler05.rb +0 -1
  21. data/lib/em-websocket/handler06.rb +0 -1
  22. data/lib/em-websocket/handler07.rb +0 -1
  23. data/lib/em-websocket/handler08.rb +0 -1
  24. data/lib/em-websocket/handler13.rb +0 -1
  25. data/lib/em-websocket/handler76.rb +2 -0
  26. data/lib/em-websocket/handshake.rb +156 -0
  27. data/lib/em-websocket/handshake04.rb +18 -16
  28. data/lib/em-websocket/handshake75.rb +15 -8
  29. data/lib/em-websocket/handshake76.rb +15 -14
  30. data/lib/em-websocket/masking04.rb +3 -6
  31. data/lib/em-websocket/message_processor_03.rb +6 -3
  32. data/lib/em-websocket/message_processor_06.rb +30 -9
  33. data/lib/em-websocket/version.rb +1 -1
  34. data/lib/em-websocket/websocket.rb +24 -15
  35. data/spec/helper.rb +84 -51
  36. data/spec/integration/common_spec.rb +89 -69
  37. data/spec/integration/draft03_spec.rb +84 -56
  38. data/spec/integration/draft05_spec.rb +14 -12
  39. data/spec/integration/draft06_spec.rb +67 -7
  40. data/spec/integration/draft13_spec.rb +30 -19
  41. data/spec/integration/draft75_spec.rb +46 -40
  42. data/spec/integration/draft76_spec.rb +59 -45
  43. data/spec/integration/gte_03_examples.rb +42 -0
  44. data/spec/integration/shared_examples.rb +119 -0
  45. data/spec/unit/framing_spec.rb +24 -4
  46. data/spec/unit/handshake_spec.rb +216 -0
  47. data/spec/unit/masking_spec.rb +2 -0
  48. metadata +32 -86
  49. data/examples/flash_policy_file_server.rb +0 -21
  50. data/examples/js/FABridge.js +0 -604
  51. data/examples/js/WebSocketMain.swf +0 -0
  52. data/examples/js/swfobject.js +0 -4
  53. data/examples/js/web_socket.js +0 -312
  54. data/lib/em-websocket/handler_factory.rb +0 -109
  55. data/spec/unit/handler_spec.rb +0 -159
@@ -1,118 +1,138 @@
1
1
  require 'helper'
2
2
 
3
3
  # These tests are not specific to any particular draft of the specification
4
- #
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