em-websocket 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,5 @@
1
1
  module EventMachine
2
2
  module Websocket
3
- VERSION = "0.4.0"
3
+ VERSION = "0.5.0"
4
4
  end
5
5
  end
@@ -17,6 +17,10 @@ module EventMachine
17
17
  def code; 1002; end
18
18
  end
19
19
 
20
+ class InvalidDataError < WSProtocolError
21
+ def code; 1007; end
22
+ end
23
+
20
24
  # 1009: Message too big to process
21
25
  class WSMessageTooBigError < WSProtocolError
22
26
  def code; 1009; end
@@ -1,7 +1,8 @@
1
+ # encoding: BINARY
2
+
1
3
  require 'rubygems'
2
4
  require 'rspec'
3
5
  require 'em-spec/rspec'
4
- require 'pp'
5
6
  require 'em-http'
6
7
 
7
8
  require 'em-websocket'
@@ -27,74 +28,79 @@ class FakeWebSocketClient < EM::Connection
27
28
  # puts "RECEIVE DATA #{data}"
28
29
  if @state == :new
29
30
  @handshake_response = data
30
- @onopen.call if @onopen
31
+ @onopen.call if defined? @onopen
31
32
  @state = :open
32
33
  else
33
- @onmessage.call(data) if @onmessage
34
+ @onmessage.call(data) if defined? @onmessage
34
35
  @packets << data
35
36
  end
36
37
  end
37
38
 
38
- def send(data)
39
- send_data("\x00#{data}\xff")
39
+ def send(application_data)
40
+ send_frame(:text, application_data)
41
+ end
42
+
43
+ def send_frame(type, application_data)
44
+ send_data construct_frame(type, application_data)
40
45
  end
41
46
 
42
47
  def unbind
43
- @onclose.call if @onclose
48
+ @onclose.call if defined? @onclose
49
+ end
50
+
51
+ private
52
+
53
+ def construct_frame(type, data)
54
+ "\x00#{data}\xff"
44
55
  end
45
56
  end
46
57
 
47
58
  class Draft03FakeWebSocketClient < FakeWebSocketClient
48
- def send(application_data)
49
- frame = ''
50
- opcode = 4 # fake only supports text frames
51
- byte1 = opcode # since more, rsv1-3 are 0
52
- frame << byte1
59
+ private
60
+
61
+ def construct_frame(type, data)
62
+ frame = ""
63
+ frame << EM::WebSocket::Framing03::FRAME_TYPES[type]
64
+ frame << encoded_length(data.size)
65
+ frame << data
66
+ end
53
67
 
54
- length = application_data.size
68
+ def encoded_length(length)
55
69
  if length <= 125
56
- byte2 = length # since rsv4 is 0
57
- frame << byte2
70
+ [length].pack('C') # since rsv4 is 0
58
71
  elsif length < 65536 # write 2 byte length
59
- frame << 126
60
- frame << [length].pack('n')
72
+ "\126#{[length].pack('n')}"
61
73
  else # write 8 byte length
62
- frame << 127
63
- frame << [length >> 32, length & 0xFFFFFFFF].pack("NN")
74
+ "\127#{[length >> 32, length & 0xFFFFFFFF].pack("NN")}"
64
75
  end
65
-
66
- frame << application_data
67
-
68
- send_data(frame)
69
76
  end
70
77
  end
71
78
 
72
- class Draft07FakeWebSocketClient < FakeWebSocketClient
73
- def send(application_data)
74
- frame = ''
75
- opcode = 1 # fake only supports text frames
76
- byte1 = opcode | 0b10000000 # since more, rsv1-3 are 0
77
- frame << byte1
79
+ class Draft05FakeWebSocketClient < Draft03FakeWebSocketClient
80
+ private
78
81
 
79
- length = application_data.size
80
- if length <= 125
81
- byte2 = length # since rsv4 is 0
82
- frame << byte2
83
- elsif length < 65536 # write 2 byte length
84
- frame << 126
85
- frame << [length].pack('n')
86
- else # write 8 byte length
87
- frame << 127
88
- frame << [length >> 32, length & 0xFFFFFFFF].pack("NN")
89
- end
82
+ def construct_frame(type, data)
83
+ frame = ""
84
+ frame << "\x00\x00\x00\x00" # Mask with nothing for simplicity
85
+ frame << (EM::WebSocket::Framing05::FRAME_TYPES[type] | 0b10000000)
86
+ frame << encoded_length(data.size)
87
+ frame << data
88
+ end
89
+ end
90
90
 
91
- frame << application_data
91
+ class Draft07FakeWebSocketClient < Draft05FakeWebSocketClient
92
+ private
92
93
 
93
- send_data(frame)
94
+ def construct_frame(type, data)
95
+ frame = ""
96
+ frame << (EM::WebSocket::Framing07::FRAME_TYPES[type] | 0b10000000)
97
+ # Should probably mask the data, but I get away without bothering since
98
+ # the server doesn't enforce that incoming frames are masked
99
+ frame << encoded_length(data.size)
100
+ frame << data
94
101
  end
95
102
  end
96
103
 
97
-
98
104
  # Wrap EM:HttpRequest in a websocket like interface so that it can be used in the specs with the same interface as FakeWebSocketClient
99
105
  class Draft75WebSocketClient
100
106
  def onopen(&blk); @onopen = blk; end
@@ -107,10 +113,10 @@ class Draft75WebSocketClient
107
113
  :timeout => 0,
108
114
  :origin => 'http://example.com',
109
115
  })
110
- @ws.errback { @onerror.call if @onerror }
111
- @ws.callback { @onopen.call if @onopen }
112
- @ws.stream { |msg| @onmessage.call(msg) if @onmessage }
113
- @ws.disconnect { @onclose.call if @onclose }
116
+ @ws.errback { @onerror.call if defined? @onerror }
117
+ @ws.callback { @onopen.call if defined? @onopen }
118
+ @ws.stream { |msg| @onmessage.call(msg) if defined? @onmessage }
119
+ @ws.disconnect { @onclose.call if defined? @onclose }
114
120
  end
115
121
 
116
122
  def send(message)
@@ -6,6 +6,12 @@ describe "WebSocket server" do
6
6
  include EM::SpecHelper
7
7
  default_timeout 1
8
8
 
9
+ def start_server
10
+ EM::WebSocket.run(:host => "0.0.0.0", :port => 12345) { |ws|
11
+ yield ws if block_given?
12
+ }
13
+ end
14
+
9
15
  it "should fail on non WebSocket requests" do
10
16
  em {
11
17
  EM.add_timer(0.1) do
@@ -14,7 +20,7 @@ describe "WebSocket server" do
14
20
  http.callback { fail }
15
21
  end
16
22
 
17
- EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) {}
23
+ start_server
18
24
  }
19
25
  end
20
26
 
@@ -30,7 +36,7 @@ describe "WebSocket server" do
30
36
  http.stream { |msg| }
31
37
  end
32
38
 
33
- EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) do |ws|
39
+ start_server do |ws|
34
40
  ws.onopen { |handshake|
35
41
  headers = handshake.headers
36
42
  headers["User-Agent"].should == "EventMachine HttpClient"
@@ -64,7 +70,7 @@ describe "WebSocket server" do
64
70
  http.stream { |msg| }
65
71
  end
66
72
 
67
- EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) do |ws|
73
+ start_server do |ws|
68
74
  ws.onopen { |handshake|
69
75
  handshake.path.should == '/hello'
70
76
  handshake.query_string.split('&').sort.
@@ -82,7 +88,7 @@ describe "WebSocket server" do
82
88
  it "should raise an exception if frame sent before handshake complete" do
83
89
  em {
84
90
  # 1. Start WebSocket server
85
- EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
91
+ start_server { |ws|
86
92
  # 3. Try to send a message to the socket
87
93
  lambda {
88
94
  ws.send('early message')
@@ -107,7 +113,7 @@ describe "WebSocket server" do
107
113
  http.stream { |msg| }
108
114
  end
109
115
 
110
- EM::WebSocket.run(:host => "0.0.0.0", :port => 12345) do |ws|
116
+ start_server do |ws|
111
117
  ws.onopen { |handshake|
112
118
  handshake.headers["User-Agent"].should == "EventMachine HttpClient"
113
119
  }
@@ -35,20 +35,21 @@ describe "draft03" do
35
35
  }
36
36
  end
37
37
 
38
- it_behaves_like "a websocket server" do
39
- let(:version) { 3 }
38
+ def start_server
39
+ EM::WebSocket.run(:host => "0.0.0.0", :port => 12345) { |ws|
40
+ yield ws if block_given?
41
+ }
42
+ end
40
43
 
41
- def start_server
42
- EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
43
- yield ws
44
- }
45
- end
44
+ def start_client
45
+ client = EM.connect('0.0.0.0', 12345, Draft03FakeWebSocketClient)
46
+ client.send_data(format_request(@request))
47
+ yield client if block_given?
48
+ return client
49
+ end
46
50
 
47
- def start_client
48
- client = EM.connect('0.0.0.0', 12345, Draft03FakeWebSocketClient)
49
- client.send_data(format_request(@request))
50
- yield client if block_given?
51
- end
51
+ it_behaves_like "a websocket server" do
52
+ let(:version) { 3 }
52
53
  end
53
54
 
54
55
  # These examples are straight from the spec
@@ -56,36 +57,30 @@ describe "draft03" do
56
57
  describe "examples from the spec" do
57
58
  it "should accept a single-frame text message" do
58
59
  em {
59
- EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
60
+ start_server { |ws|
60
61
  ws.onmessage { |msg|
61
62
  msg.should == 'Hello'
62
63
  done
63
64
  }
64
65
  }
65
-
66
- # Create a fake client which sends draft 76 handshake
67
- connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
68
- connection.send_data(format_request(@request))
69
-
70
- # Send frame
71
- connection.onopen {
72
- connection.send_data("\x04\x05Hello")
66
+ start_client { |client|
67
+ client.onopen {
68
+ client.send_data("\x04\x05Hello")
69
+ }
73
70
  }
74
71
  }
75
72
  end
76
73
 
77
74
  it "should accept a fragmented text message" do
78
75
  em {
79
- EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
76
+ start_server { |ws|
80
77
  ws.onmessage { |msg|
81
78
  msg.should == 'Hello'
82
79
  done
83
80
  }
84
81
  }
85
82
 
86
- # Create a fake client which sends draft 76 handshake
87
- connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
88
- connection.send_data(format_request(@request))
83
+ connection = start_client
89
84
 
90
85
  # Send frame
91
86
  connection.onopen {
@@ -97,11 +92,9 @@ describe "draft03" do
97
92
 
98
93
  it "should accept a ping request and respond with the same body" do
99
94
  em {
100
- EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws| }
95
+ start_server
101
96
 
102
- # Create a fake client which sends draft 76 handshake
103
- connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
104
- connection.send_data(format_request(@request))
97
+ connection = start_client
105
98
 
106
99
  # Send frame
107
100
  connection.onopen {
@@ -120,16 +113,14 @@ describe "draft03" do
120
113
  em {
121
114
  data = "a" * 256
122
115
 
123
- EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
116
+ start_server { |ws|
124
117
  ws.onmessage { |msg|
125
118
  msg.should == data
126
119
  done
127
120
  }
128
121
  }
129
122
 
130
- # Create a fake client which sends draft 76 handshake
131
- connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
132
- connection.send_data(format_request(@request))
123
+ connection = start_client
133
124
 
134
125
  # Send frame
135
126
  connection.onopen {
@@ -142,16 +133,14 @@ describe "draft03" do
142
133
  em {
143
134
  data = "a" * 65536
144
135
 
145
- EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
136
+ start_server { |ws|
146
137
  ws.onmessage { |msg|
147
138
  msg.should == data
148
139
  done
149
140
  }
150
141
  }
151
142
 
152
- # Create a fake client which sends draft 76 handshake
153
- connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
154
- connection.send_data(format_request(@request))
143
+ connection = start_client
155
144
 
156
145
  # Send frame
157
146
  connection.onopen {
@@ -164,11 +153,9 @@ describe "draft03" do
164
153
  describe "close handling" do
165
154
  it "should respond to a new close frame with a close frame" do
166
155
  em {
167
- EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws| }
156
+ start_server
168
157
 
169
- # Create a fake client which sends draft 76 handshake
170
- connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
171
- connection.send_data(format_request(@request))
158
+ connection = start_client
172
159
 
173
160
  # Send close frame
174
161
  connection.onopen {
@@ -183,22 +170,31 @@ describe "draft03" do
183
170
  }
184
171
  end
185
172
 
186
- it "should close the connection on receiving a close acknowlegement" do
173
+ 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
187
174
  em {
188
175
  ack_received = false
189
176
 
190
- EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
177
+ start_server { |ws|
191
178
  ws.onopen {
192
179
  # 2. Send a close frame
193
180
  EM.next_tick {
194
- ws.close_websocket
181
+ ws.close
195
182
  }
196
183
  }
184
+
185
+ # 5. Onclose event on server
186
+ ws.onclose { |event|
187
+ event.should == {
188
+ :code => 1005,
189
+ :reason => "",
190
+ :was_clean => true,
191
+ }
192
+ done
193
+ }
197
194
  }
198
195
 
199
196
  # 1. Create a fake client which sends draft 76 handshake
200
- connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
201
- connection.send_data(format_request(@request))
197
+ connection = start_client
202
198
 
203
199
  # 3. Check that close frame recieved and acknowlege it
204
200
  connection.onmessage { |frame|
@@ -210,18 +206,39 @@ describe "draft03" do
210
206
  # 4. Check that connection is closed _after_ the ack
211
207
  connection.onclose {
212
208
  ack_received.should == true
213
- done
209
+ }
210
+ }
211
+ end
212
+
213
+ # it "should repur"
214
+ #
215
+ it "should return close code 1005 and was_clean=true after closing handshake (initiated by client)" do
216
+ em {
217
+ start_server { |ws|
218
+ ws.onclose { |event|
219
+ event.should == {
220
+ :code => 1005,
221
+ :reason => "",
222
+ :was_clean => true,
223
+ }
224
+ done
225
+ }
226
+ }
227
+ start_client { |client|
228
+ client.onopen {
229
+ client.send_data("\x01\x00")
230
+ }
214
231
  }
215
232
  }
216
233
  end
217
234
 
218
235
  it "should not allow data frame to be sent after close frame sent" do
219
236
  em {
220
- EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
237
+ start_server { |ws|
221
238
  ws.onopen {
222
239
  # 2. Send a close frame
223
240
  EM.next_tick {
224
- ws.close_websocket
241
+ ws.close
225
242
  }
226
243
 
227
244
  # 3. Check that exception raised if I attempt to send more data
@@ -235,25 +252,23 @@ describe "draft03" do
235
252
  }
236
253
 
237
254
  # 1. Create a fake client which sends draft 76 handshake
238
- connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
239
- connection.send_data(format_request(@request))
255
+ start_client
240
256
  }
241
257
  end
242
258
 
243
259
  it "should still respond to control frames after close frame sent" do
244
260
  em {
245
- EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
261
+ start_server { |ws|
246
262
  ws.onopen {
247
263
  # 2. Send a close frame
248
264
  EM.next_tick {
249
- ws.close_websocket
265
+ ws.close
250
266
  }
251
267
  }
252
268
  }
253
269
 
254
270
  # 1. Create a fake client which sends draft 76 handshake
255
- connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
256
- connection.send_data(format_request(@request))
271
+ connection = start_client
257
272
 
258
273
  connection.onmessage { |frame|
259
274
  if frame == "\x01\x00"
@@ -268,5 +283,17 @@ describe "draft03" do
268
283
  }
269
284
  }
270
285
  end
286
+
287
+ it "should report that close codes are not supported" do
288
+ em {
289
+ start_server { |ws|
290
+ ws.onopen {
291
+ ws.supports_close_codes?.should == false
292
+ done
293
+ }
294
+ }
295
+ start_client
296
+ }
297
+ end
271
298
  end
272
299
  end