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.
- checksums.yaml +7 -0
- data/CHANGELOG.rdoc +52 -0
- data/Gemfile +6 -0
- data/LICENCE +7 -0
- data/README.md +105 -40
- data/examples/echo.rb +22 -6
- data/examples/test.html +5 -6
- data/lib/em-websocket.rb +2 -1
- 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 +154 -48
- data/lib/em-websocket/framing03.rb +3 -2
- data/lib/em-websocket/framing05.rb +3 -2
- data/lib/em-websocket/framing07.rb +16 -4
- data/lib/em-websocket/framing76.rb +1 -4
- data/lib/em-websocket/handler.rb +61 -15
- 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 -16
- data/lib/em-websocket/handshake75.rb +15 -8
- data/lib/em-websocket/handshake76.rb +15 -14
- data/lib/em-websocket/masking04.rb +3 -6
- data/lib/em-websocket/message_processor_03.rb +6 -3
- data/lib/em-websocket/message_processor_06.rb +30 -9
- data/lib/em-websocket/version.rb +1 -1
- data/lib/em-websocket/websocket.rb +24 -15
- data/spec/helper.rb +84 -51
- data/spec/integration/common_spec.rb +89 -69
- data/spec/integration/draft03_spec.rb +84 -56
- data/spec/integration/draft05_spec.rb +14 -12
- data/spec/integration/draft06_spec.rb +67 -7
- data/spec/integration/draft13_spec.rb +30 -19
- data/spec/integration/draft75_spec.rb +46 -40
- data/spec/integration/draft76_spec.rb +59 -45
- data/spec/integration/gte_03_examples.rb +42 -0
- data/spec/integration/shared_examples.rb +119 -0
- data/spec/unit/framing_spec.rb +24 -4
- data/spec/unit/handshake_spec.rb +216 -0
- data/spec/unit/masking_spec.rb +2 -0
- metadata +32 -86
- 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 -109
- 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
|
-
|
12
|
-
http =
|
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
|
-
|
17
|
+
start_server
|
18
18
|
}
|
19
19
|
end
|
20
|
-
|
21
|
-
it "should
|
20
|
+
|
21
|
+
it "should expose the WebSocket request headers, path and query params" do
|
22
22
|
em {
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
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
|
-
|
34
|
-
ws.onopen {
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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
|
-
|
43
|
+
done
|
45
44
|
}
|
46
45
|
end
|
47
46
|
}
|
48
47
|
end
|
49
|
-
|
50
|
-
it "should
|
48
|
+
|
49
|
+
it "should expose the WebSocket path and query params when nonempty" do
|
51
50
|
em {
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
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
|
-
|
57
|
+
ws.stream { |msg| }
|
60
58
|
end
|
61
59
|
|
62
|
-
|
63
|
-
ws.onopen {
|
64
|
-
path
|
65
|
-
|
66
|
-
|
67
|
-
|
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
|
-
|
69
|
+
done
|
73
70
|
}
|
74
71
|
end
|
75
72
|
}
|
76
73
|
end
|
77
|
-
|
78
|
-
it "should
|
74
|
+
|
75
|
+
it "should raise an exception if frame sent before handshake complete" do
|
79
76
|
em {
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
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
|
-
|
91
|
-
ws.onopen {
|
92
|
-
|
93
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
40
|
-
|
41
|
-
yield ws
|
42
|
-
}
|
43
|
-
end
|
45
|
+
let(:version) { 3 }
|
46
|
+
end
|
44
47
|
|
45
|
-
|
46
|
-
|
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
|
-
|
57
|
+
start_server { |ws|
|
58
58
|
ws.onmessage { |msg|
|
59
59
|
msg.should == 'Hello'
|
60
60
|
done
|
61
61
|
}
|
62
62
|
}
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
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
|
-
|
73
|
+
start_server { |ws|
|
78
74
|
ws.onmessage { |msg|
|
79
75
|
msg.should == 'Hello'
|
80
76
|
done
|
81
77
|
}
|
82
78
|
}
|
83
79
|
|
84
|
-
|
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
|
-
|
92
|
+
start_server
|
99
93
|
|
100
|
-
|
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
|
-
|
122
|
-
ws.
|
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
|
-
|
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
|
-
|
144
|
-
ws.
|
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
|
-
|
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
|
-
|
155
|
+
start_server
|
166
156
|
|
167
|
-
|
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
|
-
|
176
|
+
start_server { |ws|
|
189
177
|
ws.onopen {
|
190
178
|
# 2. Send a close frame
|
191
179
|
EM.next_tick {
|
192
|
-
ws.
|
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 =
|
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
|
-
|
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
|
-
|
236
|
+
start_server { |ws|
|
219
237
|
ws.onopen {
|
220
238
|
# 2. Send a close frame
|
221
239
|
EM.next_tick {
|
222
|
-
ws.
|
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
|
-
|
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
|
-
|
260
|
+
start_server { |ws|
|
244
261
|
ws.onopen {
|
245
262
|
# 2. Send a close frame
|
246
263
|
EM.next_tick {
|
247
|
-
ws.
|
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 =
|
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
|