em-websocket 0.4.0 → 0.5.0

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.
@@ -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