em-websocket 0.1.4 → 0.2.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.
@@ -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