em-websocket 0.1.4 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,252 @@
1
+ require 'spec/helper'
2
+
3
+ describe "draft03" do
4
+ before :each do
5
+ @request = {
6
+ :port => 80,
7
+ :method => "GET",
8
+ :path => "/demo",
9
+ :headers => {
10
+ 'Host' => 'example.com',
11
+ 'Connection' => 'Upgrade',
12
+ 'Sec-WebSocket-Key2' => '12998 5 Y3 1 .P00',
13
+ 'Sec-WebSocket-Protocol' => 'sample',
14
+ 'Upgrade' => 'WebSocket',
15
+ 'Sec-WebSocket-Key1' => '4 @1 46546xW%0l 1 5',
16
+ 'Origin' => 'http://example.com',
17
+ 'Sec-WebSocket-Draft' => '3'
18
+ },
19
+ :body => '^n:ds[4U'
20
+ }
21
+
22
+ @response = {
23
+ :headers => {
24
+ "Upgrade" => "WebSocket",
25
+ "Connection" => "Upgrade",
26
+ "Sec-WebSocket-Location" => "ws://example.com/demo",
27
+ "Sec-WebSocket-Origin" => "http://example.com",
28
+ "Sec-WebSocket-Protocol" => "sample"
29
+ },
30
+ :body => "8jKS\'y:G*Co,Wxa-"
31
+ }
32
+ end
33
+
34
+ # These examples are straight from the spec
35
+ # http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-03#section-4.6
36
+ describe "examples from the spec" do
37
+ it "should accept a single-frame text message" do
38
+ EM.run do
39
+ EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
40
+ ws.onmessage { |msg|
41
+ msg.should == 'Hello'
42
+ EM.stop
43
+ }
44
+ }
45
+
46
+ # Create a fake client which sends draft 76 handshake
47
+ connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
48
+ connection.send_data(format_request(@request))
49
+
50
+ # Send frame
51
+ connection.onopen = lambda {
52
+ connection.send_data("\x04\x05Hello")
53
+ }
54
+ end
55
+ end
56
+
57
+ it "should accept a fragmented text message" do
58
+ EM.run do
59
+ EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
60
+ ws.onmessage { |msg|
61
+ msg.should == 'Hello'
62
+ EM.stop
63
+ }
64
+ }
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 = lambda {
72
+ connection.send_data("\x84\x03Hel")
73
+ connection.send_data("\x00\x02lo")
74
+ }
75
+ end
76
+ end
77
+
78
+ it "should accept a ping request and respond with the same body" do
79
+ EM.run do
80
+ EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws| }
81
+
82
+ # Create a fake client which sends draft 76 handshake
83
+ connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
84
+ connection.send_data(format_request(@request))
85
+
86
+ # Send frame
87
+ connection.onopen = lambda {
88
+ connection.send_data("\x02\x05Hello")
89
+ }
90
+
91
+ connection.onmessage = lambda { |frame|
92
+ next if frame.nil?
93
+ frame.should == "\x03\x05Hello"
94
+ EM.stop
95
+ }
96
+ end
97
+ end
98
+
99
+ it "should accept a 256 bytes binary message in a single frame" do
100
+ EM.run do
101
+ data = "a" * 256
102
+
103
+ EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
104
+ ws.onmessage { |msg|
105
+ msg.should == data
106
+ EM.stop
107
+ }
108
+ }
109
+
110
+ # Create a fake client which sends draft 76 handshake
111
+ connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
112
+ connection.send_data(format_request(@request))
113
+
114
+ # Send frame
115
+ connection.onopen = lambda {
116
+ connection.send_data("\x05\x7E\x01\x00" + data)
117
+ }
118
+ end
119
+ end
120
+
121
+ it "should accept a 64KiB binary message in a single frame" do
122
+ EM.run do
123
+ data = "a" * 65536
124
+
125
+ EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
126
+ ws.onmessage { |msg|
127
+ msg.should == data
128
+ EM.stop
129
+ }
130
+ }
131
+
132
+ # Create a fake client which sends draft 76 handshake
133
+ connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
134
+ connection.send_data(format_request(@request))
135
+
136
+ # Send frame
137
+ connection.onopen = lambda {
138
+ connection.send_data("\x05\x7F\x00\x00\x00\x00\x00\x01\x00\x00" + data)
139
+ }
140
+ end
141
+ end
142
+ end
143
+
144
+ describe "close handling" do
145
+ it "should respond to a new close frame with a close frame" do
146
+ EM.run do
147
+ EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws| }
148
+
149
+ # Create a fake client which sends draft 76 handshake
150
+ connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
151
+ connection.send_data(format_request(@request))
152
+
153
+ # Send close frame
154
+ connection.onopen = lambda {
155
+ connection.send_data("\x01\x00")
156
+ }
157
+
158
+ # Check that close ack received
159
+ connection.onmessage = lambda { |frame|
160
+ frame.should == "\x01\x00"
161
+ EM.stop
162
+ }
163
+ end
164
+ end
165
+
166
+ it "should close the connection on receiving a close acknowlegement" do
167
+ EM.run do
168
+ ack_received = false
169
+
170
+ EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
171
+ ws.onopen {
172
+ # 2. Send a close frame
173
+ EM.next_tick {
174
+ ws.close_websocket
175
+ }
176
+ }
177
+ }
178
+
179
+ # 1. Create a fake client which sends draft 76 handshake
180
+ connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
181
+ connection.send_data(format_request(@request))
182
+
183
+ # 3. Check that close frame recieved and acknowlege it
184
+ connection.onmessage = lambda { |frame|
185
+ frame.should == "\x01\x00"
186
+ ack_received = true
187
+ connection.send_data("\x01\x00")
188
+ }
189
+
190
+ # 4. Check that connection is closed _after_ the ack
191
+ connection.onclose = lambda {
192
+ ack_received.should == true
193
+ EM.stop
194
+ }
195
+ end
196
+ end
197
+
198
+ it "should not allow data fraome to be sent after close frame sent" do
199
+ EM.run {
200
+ EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
201
+ ws.onopen {
202
+ # 2. Send a close frame
203
+ EM.next_tick {
204
+ ws.close_websocket
205
+ }
206
+
207
+ # 3. Check that exception raised if I attempt to send more data
208
+ EM.add_timer(0.1) {
209
+ lambda {
210
+ ws.send('hello world')
211
+ }.should raise_error(EM::WebSocket::WebSocketError, 'Cannot send data frame since connection is closing')
212
+ EM.stop
213
+ }
214
+ }
215
+ }
216
+
217
+ # 1. Create a fake client which sends draft 76 handshake
218
+ connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
219
+ connection.send_data(format_request(@request))
220
+ }
221
+ end
222
+
223
+ it "should still respond to control frames after close frame sent" do
224
+ EM.run {
225
+ EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
226
+ ws.onopen {
227
+ # 2. Send a close frame
228
+ EM.next_tick {
229
+ ws.close_websocket
230
+ }
231
+ }
232
+ }
233
+
234
+ # 1. Create a fake client which sends draft 76 handshake
235
+ connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
236
+ connection.send_data(format_request(@request))
237
+
238
+ connection.onmessage = lambda { |frame|
239
+ if frame == "\x01\x00"
240
+ # 3. After the close frame is received send a ping frame, but
241
+ # don't respond with a close ack
242
+ connection.send_data("\x02\x05Hello")
243
+ else
244
+ # 4. Check that the pong is received
245
+ frame.should == "\x03\x05Hello"
246
+ EM.stop
247
+ end
248
+ }
249
+ }
250
+ end
251
+ end
252
+ end
@@ -161,7 +161,32 @@ describe "WebSocket server draft76" do
161
161
  }
162
162
 
163
163
  client = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
164
- client.send_data("This is not a HTTP header")
164
+ client.send_data("This is not a HTTP header\r\n\r\n")
165
165
  }
166
166
  end
167
+
168
+ it "should handle handshake request split into two TCP packets" do
169
+ EM.run do
170
+ EM.add_timer(0.1) do
171
+ EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { }
172
+
173
+ # Create a fake client which sends draft 76 handshake
174
+ connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
175
+ data = format_request(@request)
176
+ # Sends first half of the request
177
+ connection.send_data(data[0...(data.length / 2)])
178
+
179
+ connection.onopen = lambda {
180
+ connection.handshake_response.lines.sort.
181
+ should == format_response(@response).lines.sort
182
+ EM.stop
183
+ }
184
+
185
+ EM.add_timer(0.1) do
186
+ # Sends second half of the request
187
+ connection.send_data(data[(data.length / 2)..-1])
188
+ end
189
+ end
190
+ end
191
+ end
167
192
  end
@@ -0,0 +1,108 @@
1
+ require 'spec/helper'
2
+
3
+ describe EM::WebSocket::Framing03 do
4
+ class FramingContainer
5
+ include EM::WebSocket::Framing03
6
+
7
+ def <<(data)
8
+ @data << data
9
+ process_data
10
+ end
11
+
12
+ def debug(*args); end
13
+ end
14
+
15
+ before :each do
16
+ @f = FramingContainer.new
17
+ @f.initialize_framing
18
+ end
19
+
20
+ describe "basic examples" do
21
+ it "connection close" do
22
+ @f.should_receive(:message).with(:close, '', '')
23
+ @f << 0b00000001
24
+ @f << 0b00000000
25
+ end
26
+
27
+ it "ping" do
28
+ @f.should_receive(:message).with(:ping, '', '')
29
+ @f << 0b00000010
30
+ @f << 0b00000000
31
+ end
32
+
33
+ it "pong" do
34
+ @f.should_receive(:message).with(:pong, '', '')
35
+ @f << 0b00000011
36
+ @f << 0b00000000
37
+ end
38
+
39
+ it "text" do
40
+ @f.should_receive(:message).with(:text, '', 'foo')
41
+ @f << 0b00000100
42
+ @f << 0b00000011
43
+ @f << 'foo'
44
+ end
45
+
46
+ it "Text in two frames" do
47
+ @f.should_receive(:message).with(:text, '', 'hello world')
48
+ @f << 0b10000100
49
+ @f << 0b00000110
50
+ @f << "hello "
51
+ @f << 0b00000000
52
+ @f << 0b00000101
53
+ @f << "world"
54
+ end
55
+
56
+ it "2 byte extended payload length text frame" do
57
+ data = 'a' * 256
58
+ @f.should_receive(:message).with(:text, '', data)
59
+ @f << 0b00000100 # Single frame, text
60
+ @f << 0b01111110 # Length 126 (so read 2 bytes)
61
+ @f << 0b00000001 # Two bytes in network byte order (256)
62
+ @f << 0b00000000
63
+ @f << data
64
+ end
65
+ end
66
+
67
+ # These examples are straight from the spec
68
+ # http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-03#section-4.6
69
+ describe "examples from the spec" do
70
+ it "a single-frame text message" do
71
+ @f.should_receive(:message).with(:text, '', 'Hello')
72
+ @f << "\x04\x05Hello"
73
+ end
74
+
75
+ it "a fragmented text message" do
76
+ @f.should_receive(:message).with(:text, '', 'Hello')
77
+ @f << "\x84\x03Hel"
78
+ @f << "\x00\x02lo"
79
+ end
80
+
81
+ it "Ping request and response" do
82
+ @f.should_receive(:message).with(:ping, '', 'Hello')
83
+ @f << "\x02\x05Hello"
84
+ end
85
+
86
+ it "256 bytes binary message in a single frame" do
87
+ data = "a"*256
88
+ @f.should_receive(:message).with(:binary, '', data)
89
+ @f << "\x05\x7E\x01\x00" + data
90
+ end
91
+
92
+ it "64KiB binary message in a single frame" do
93
+ data = "a"*65536
94
+ @f.should_receive(:message).with(:binary, '', data)
95
+ @f << "\x05\x7F\x00\x00\x00\x00\x00\x01\x00\x00" + data
96
+ end
97
+ end
98
+
99
+ describe "error cases" do
100
+ it "should raise an exception on continuation frame without preceeding more frame" do
101
+ lambda {
102
+ @f << 0b00000000 # Single frame, continuation
103
+ @f << 0b00000001 # Length 1
104
+ @f << 'f'
105
+ }.should raise_error(EM::WebSocket::WebSocketError, 'Continuation frame not expected')
106
+ end
107
+ end
108
+ end
@@ -124,4 +124,16 @@ describe "EventMachine::WebSocket::Handler" do
124
124
  }.should raise_error(EM::WebSocket::HandshakeError, 'Websocket Key1 or Key2 does not contain spaces - this is a symptom of a cross-protocol attack')
125
125
  end
126
126
  end
127
+
128
+ it "should leave request with incomplete header" do
129
+ data = format_request(@request)
130
+ # Sends only half of the request
131
+ EM::WebSocket::HandlerFactory.build(mock(EM::WebSocket::Connection), data[0...(data.length / 2)]).should == nil
132
+ end
133
+
134
+ it "should leave request with incomplete third key" do
135
+ data = format_request(@request)
136
+ # Removes last two bytes of the third key
137
+ EM::WebSocket::HandlerFactory.build(mock(EM::WebSocket::Connection), data[0...(data.length - 2)]).should == nil
138
+ end
127
139
  end
@@ -190,4 +190,19 @@ describe EventMachine::WebSocket do
190
190
  end
191
191
  end
192
192
 
193
- end
193
+ it "should raise an exception if frame sent before handshake complete" do
194
+ EM.run {
195
+ EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |c|
196
+ # We're not using a real client so the handshake will not be sent
197
+ EM.add_timer(0.1) {
198
+ lambda {
199
+ c.send('early message')
200
+ }.should raise_error('Cannot send data before onopen callback')
201
+ EM.stop
202
+ }
203
+ }
204
+
205
+ client = EM.connect('0.0.0.0', 12345, EM::Connection)
206
+ }
207
+ end
208
+ end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 1
8
- - 4
9
- version: 0.1.4
7
+ - 2
8
+ - 0
9
+ version: 0.2.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Ilya Grigorik
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-08-23 00:00:00 -04:00
17
+ date: 2010-11-23 00:00:00 +00:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -50,7 +50,7 @@ dependencies:
50
50
  prerelease: false
51
51
  requirement: &id003 !ruby/object:Gem::Requirement
52
52
  requirements:
53
- - - ">="
53
+ - - ~>
54
54
  - !ruby/object:Gem::Version
55
55
  segments:
56
56
  - 0
@@ -59,20 +59,47 @@ dependencies:
59
59
  version: 0.2.6
60
60
  type: :development
61
61
  version_requirements: *id003
62
+ - !ruby/object:Gem::Dependency
63
+ name: rspec
64
+ prerelease: false
65
+ requirement: &id004 !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ segments:
70
+ - 2
71
+ - 0
72
+ - 0
73
+ version: 2.0.0
74
+ type: :development
75
+ version_requirements: *id004
76
+ - !ruby/object:Gem::Dependency
77
+ name: rake
78
+ prerelease: false
79
+ requirement: &id005 !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ segments:
84
+ - 0
85
+ version: "0"
86
+ type: :development
87
+ version_requirements: *id005
62
88
  description: EventMachine based WebSocket server
63
- email: ilya@igvita.com
89
+ email:
90
+ - ilya@igvita.com
64
91
  executables: []
65
92
 
66
93
  extensions: []
67
94
 
68
- extra_rdoc_files:
69
- - README.rdoc
95
+ extra_rdoc_files: []
96
+
70
97
  files:
71
98
  - .gitignore
72
99
  - CHANGELOG.rdoc
73
- - README.rdoc
100
+ - Gemfile
101
+ - README.md
74
102
  - Rakefile
75
- - VERSION
76
103
  - em-websocket.gemspec
77
104
  - examples/echo.rb
78
105
  - examples/js/FABridge.js
@@ -84,13 +111,21 @@ files:
84
111
  - lib/em-websocket.rb
85
112
  - lib/em-websocket/connection.rb
86
113
  - lib/em-websocket/debugger.rb
114
+ - lib/em-websocket/framing03.rb
115
+ - lib/em-websocket/framing76.rb
87
116
  - lib/em-websocket/handler.rb
117
+ - lib/em-websocket/handler03.rb
88
118
  - lib/em-websocket/handler75.rb
89
119
  - lib/em-websocket/handler76.rb
90
120
  - lib/em-websocket/handler_factory.rb
121
+ - lib/em-websocket/handshake75.rb
122
+ - lib/em-websocket/handshake76.rb
123
+ - lib/em-websocket/version.rb
91
124
  - lib/em-websocket/websocket.rb
92
125
  - spec/helper.rb
93
- - spec/integration/integration_spec.rb
126
+ - spec/integration/draft03_spec.rb
127
+ - spec/integration/draft76_spec.rb
128
+ - spec/unit/framing_spec.rb
94
129
  - spec/unit/handler_spec.rb
95
130
  - spec/websocket_spec.rb
96
131
  has_rdoc: true
@@ -98,8 +133,8 @@ homepage: http://github.com/igrigorik/em-websocket
98
133
  licenses: []
99
134
 
100
135
  post_install_message:
101
- rdoc_options:
102
- - --charset=UTF-8
136
+ rdoc_options: []
137
+
103
138
  require_paths:
104
139
  - lib
105
140
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -125,9 +160,8 @@ specification_version: 3
125
160
  summary: EventMachine based WebSocket server
126
161
  test_files:
127
162
  - spec/helper.rb
128
- - spec/integration/integration_spec.rb
163
+ - spec/integration/draft03_spec.rb
164
+ - spec/integration/draft76_spec.rb
165
+ - spec/unit/framing_spec.rb
129
166
  - spec/unit/handler_spec.rb
130
167
  - spec/websocket_spec.rb
131
- - examples/echo.rb
132
- - examples/multicast.rb
133
- - examples/srv.rb