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.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.rdoc +52 -0
  3. data/Gemfile +6 -0
  4. data/LICENCE +7 -0
  5. data/README.md +105 -40
  6. data/examples/echo.rb +22 -6
  7. data/examples/test.html +5 -6
  8. data/lib/em-websocket.rb +2 -1
  9. data/lib/em-websocket/close03.rb +3 -0
  10. data/lib/em-websocket/close05.rb +3 -0
  11. data/lib/em-websocket/close06.rb +3 -0
  12. data/lib/em-websocket/close75.rb +2 -1
  13. data/lib/em-websocket/connection.rb +154 -48
  14. data/lib/em-websocket/framing03.rb +3 -2
  15. data/lib/em-websocket/framing05.rb +3 -2
  16. data/lib/em-websocket/framing07.rb +16 -4
  17. data/lib/em-websocket/framing76.rb +1 -4
  18. data/lib/em-websocket/handler.rb +61 -15
  19. data/lib/em-websocket/handler03.rb +0 -1
  20. data/lib/em-websocket/handler05.rb +0 -1
  21. data/lib/em-websocket/handler06.rb +0 -1
  22. data/lib/em-websocket/handler07.rb +0 -1
  23. data/lib/em-websocket/handler08.rb +0 -1
  24. data/lib/em-websocket/handler13.rb +0 -1
  25. data/lib/em-websocket/handler76.rb +2 -0
  26. data/lib/em-websocket/handshake.rb +156 -0
  27. data/lib/em-websocket/handshake04.rb +18 -16
  28. data/lib/em-websocket/handshake75.rb +15 -8
  29. data/lib/em-websocket/handshake76.rb +15 -14
  30. data/lib/em-websocket/masking04.rb +3 -6
  31. data/lib/em-websocket/message_processor_03.rb +6 -3
  32. data/lib/em-websocket/message_processor_06.rb +30 -9
  33. data/lib/em-websocket/version.rb +1 -1
  34. data/lib/em-websocket/websocket.rb +24 -15
  35. data/spec/helper.rb +84 -51
  36. data/spec/integration/common_spec.rb +89 -69
  37. data/spec/integration/draft03_spec.rb +84 -56
  38. data/spec/integration/draft05_spec.rb +14 -12
  39. data/spec/integration/draft06_spec.rb +67 -7
  40. data/spec/integration/draft13_spec.rb +30 -19
  41. data/spec/integration/draft75_spec.rb +46 -40
  42. data/spec/integration/draft76_spec.rb +59 -45
  43. data/spec/integration/gte_03_examples.rb +42 -0
  44. data/spec/integration/shared_examples.rb +119 -0
  45. data/spec/unit/framing_spec.rb +24 -4
  46. data/spec/unit/handshake_spec.rb +216 -0
  47. data/spec/unit/masking_spec.rb +2 -0
  48. metadata +32 -86
  49. data/examples/flash_policy_file_server.rb +0 -21
  50. data/examples/js/FABridge.js +0 -604
  51. data/examples/js/WebSocketMain.swf +0 -0
  52. data/examples/js/swfobject.js +0 -4
  53. data/examples/js/web_socket.js +0 -312
  54. data/lib/em-websocket/handler_factory.rb +0 -109
  55. data/spec/unit/handler_spec.rb +0 -159
@@ -20,28 +20,30 @@ describe "draft05" do
20
20
  }
21
21
  }
22
22
  end
23
-
24
- def start_server
25
- EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
26
- yield ws
27
- }
28
- end
29
23
 
30
24
  def start_client
31
- client = EM.connect('0.0.0.0', 12345, Draft03FakeWebSocketClient)
25
+ client = EM.connect('0.0.0.0', 12345, Draft05FakeWebSocketClient)
32
26
  client.send_data(format_request(@request))
33
27
  yield client if block_given?
28
+ return client
34
29
  end
35
30
 
36
- it "should open connection" do
31
+ it_behaves_like "a websocket server" do
32
+ let(:version) { 5 }
33
+ end
34
+
35
+ it_behaves_like "a WebSocket server drafts 3 and above" do
36
+ let(:version) { 5 }
37
+ end
38
+
39
+ it "should report that close codes are not supported" do
37
40
  em {
38
- start_server { |server|
39
- server.onopen {
40
- server.instance_variable_get(:@handler).class.should == EventMachine::WebSocket::Handler05
41
+ start_server { |ws|
42
+ ws.onopen {
43
+ ws.supports_close_codes?.should == false
41
44
  done
42
45
  }
43
46
  }
44
-
45
47
  start_client
46
48
  }
47
49
  end
@@ -26,22 +26,26 @@ describe "draft06" do
26
26
  "Upgrade" => "websocket",
27
27
  "Connection" => "Upgrade",
28
28
  "Sec-WebSocket-Accept" => "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=",
29
+ "Sec-WebSocket-Protocol" => "sample",
29
30
  }
30
31
  }
31
32
  end
32
-
33
- def start_server
34
- EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
35
- yield ws
36
- }
37
- end
38
33
 
39
34
  def start_client
40
- client = EM.connect('0.0.0.0', 12345, Draft03FakeWebSocketClient)
35
+ client = EM.connect('0.0.0.0', 12345, Draft05FakeWebSocketClient)
41
36
  client.send_data(format_request(@request))
42
37
  yield client if block_given?
38
+ return client
43
39
  end
44
40
 
41
+ it_behaves_like "a websocket server" do
42
+ let(:version) { 6 }
43
+ end
44
+
45
+ it_behaves_like "a WebSocket server drafts 3 and above" do
46
+ let(:version) { 6 }
47
+ end
48
+
45
49
  it "should open connection" do
46
50
  em {
47
51
  start_server { |server|
@@ -82,4 +86,60 @@ describe "draft06" do
82
86
  }
83
87
  }
84
88
  end
89
+
90
+ it "should return close code and reason if closed via handshake" do
91
+ em {
92
+ start_server { |ws|
93
+ ws.onclose { |event|
94
+ # 2. Receive close event in server
95
+ event.should == {
96
+ :code => 4004,
97
+ :reason => "close reason",
98
+ :was_clean => true,
99
+ }
100
+ done
101
+ }
102
+ }
103
+ start_client { |client|
104
+ client.onopen {
105
+ # 1: Send close handshake
106
+ close_data = [4004].pack('n')
107
+ close_data << "close reason"
108
+ client.send_frame(:close, close_data)
109
+ }
110
+ }
111
+ }
112
+ end
113
+
114
+ it "should return close code 1005 if no code was specified" do
115
+ em {
116
+ start_server { |ws|
117
+ ws.onclose { |event|
118
+ event.should == {
119
+ :code => 1005,
120
+ :reason => "",
121
+ :was_clean => true,
122
+ }
123
+ done
124
+ }
125
+ }
126
+ start_client { |client|
127
+ client.onopen {
128
+ client.send_frame(:close, '')
129
+ }
130
+ }
131
+ }
132
+ end
133
+
134
+ it "should report that close codes are supported" do
135
+ em {
136
+ start_server { |ws|
137
+ ws.onopen {
138
+ ws.supports_close_codes?.should == true
139
+ done
140
+ }
141
+ }
142
+ start_client
143
+ }
144
+ end
85
145
  end
@@ -1,5 +1,6 @@
1
+ # encoding: BINARY
2
+
1
3
  require 'helper'
2
- require 'integration/shared_examples'
3
4
 
4
5
  describe "draft13" do
5
6
  include EM::SpecHelper
@@ -27,31 +28,31 @@ describe "draft13" do
27
28
  "Upgrade" => "websocket",
28
29
  "Connection" => "Upgrade",
29
30
  "Sec-WebSocket-Accept" => "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=",
31
+ "Sec-WebSocket-Protocol" => "sample",
30
32
  }
31
33
  }
32
34
  end
33
35
 
36
+ def start_client
37
+ client = EM.connect('0.0.0.0', 12345, Draft07FakeWebSocketClient)
38
+ client.send_data(format_request(@request))
39
+ yield client if block_given?
40
+ return client
41
+ end
42
+
34
43
  it_behaves_like "a websocket server" do
35
- def start_server
36
- EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
37
- yield ws
38
- }
39
- end
44
+ let(:version) { 13 }
45
+ end
40
46
 
41
- def start_client
42
- client = EM.connect('0.0.0.0', 12345, Draft07FakeWebSocketClient)
43
- client.send_data(format_request(@request))
44
- yield client if block_given?
45
- end
47
+ it_behaves_like "a WebSocket server drafts 3 and above" do
48
+ let(:version) { 13 }
46
49
  end
47
50
 
48
51
  it "should send back the correct handshake response" do
49
52
  em {
50
- EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { }
53
+ start_server
51
54
 
52
- # Create a fake client which sends draft 13 handshake
53
- connection = EM.connect('0.0.0.0', 12345, Draft07FakeWebSocketClient)
54
- connection.send_data(format_request(@request))
55
+ connection = start_client
55
56
 
56
57
  connection.onopen {
57
58
  connection.handshake_response.lines.sort.
@@ -64,7 +65,7 @@ describe "draft13" do
64
65
  # TODO: This test would be much nicer with a real websocket client...
65
66
  it "should support sending pings and binding to onpong" do
66
67
  em {
67
- EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
68
+ start_server { |ws|
68
69
  ws.onopen {
69
70
  ws.should be_pingable
70
71
  EM.next_tick {
@@ -78,9 +79,7 @@ describe "draft13" do
78
79
  }
79
80
  }
80
81
 
81
- # Create a fake client which sends draft 13 handshake
82
- connection = EM.connect('0.0.0.0', 12345, Draft07FakeWebSocketClient)
83
- connection.send_data(format_request(@request))
82
+ connection = start_client
84
83
 
85
84
  # Confusing, fake onmessage means any data after the handshake
86
85
  connection.onmessage { |data|
@@ -91,4 +90,16 @@ describe "draft13" do
91
90
  }
92
91
  }
93
92
  end
93
+
94
+ it "should report that close codes are supported" do
95
+ em {
96
+ start_server { |ws|
97
+ ws.onopen {
98
+ ws.supports_close_codes?.should == true
99
+ done
100
+ }
101
+ }
102
+ start_client
103
+ }
104
+ end
94
105
  end
@@ -1,5 +1,4 @@
1
1
  require 'helper'
2
- require 'integration/shared_examples'
3
2
 
4
3
  # These integration tests are older and use a different testing style to the
5
4
  # integration tests for newer drafts. They use EM::HttpRequest which happens
@@ -9,38 +8,35 @@ describe "WebSocket server draft75" do
9
8
  include EM::SpecHelper
10
9
  default_timeout 1
11
10
 
12
- it_behaves_like "a websocket server" do
13
- def start_server
14
- EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
15
- yield ws
16
- }
17
- end
11
+ def start_client
12
+ client = Draft75WebSocketClient.new
13
+ yield client if block_given?
14
+ return client
15
+ end
18
16
 
19
- def start_client
20
- client = Draft75WebSocketClient.new
21
- yield client if block_given?
22
- end
17
+ it_behaves_like "a websocket server" do
18
+ let(:version) { 75 }
23
19
  end
24
20
 
25
21
  it "should automatically complete WebSocket handshake" do
26
22
  em {
27
23
  MSG = "Hello World!"
28
24
  EventMachine.add_timer(0.1) do
29
- http = EventMachine::HttpRequest.new('ws://127.0.0.1:12345/').get :timeout => 0
30
- http.errback { fail }
31
- http.callback { http.response_header.status.should == 101 }
25
+ ws = EventMachine::WebSocketClient.connect('ws://127.0.0.1:12345/')
26
+ ws.errback { fail }
27
+ ws.callback { }
32
28
 
33
- http.stream { |msg|
34
- msg.should == MSG
29
+ ws.stream { |msg|
30
+ msg.data.should == MSG
35
31
  EventMachine.stop
36
32
  }
37
33
  end
38
34
 
39
- EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) do |ws|
35
+ start_server { |ws|
40
36
  ws.onopen {
41
37
  ws.send MSG
42
38
  }
43
- end
39
+ }
44
40
  }
45
41
  end
46
42
 
@@ -50,17 +46,16 @@ describe "WebSocket server draft75" do
50
46
  received = []
51
47
 
52
48
  EventMachine.add_timer(0.1) do
53
- http = EventMachine::HttpRequest.new('ws://127.0.0.1:12345/').get :timeout => 0
54
- http.errback { fail }
55
- http.stream {|msg|}
56
- http.callback {
57
- http.response_header.status.should == 101
58
- http.send messages[0]
59
- http.send messages[1]
49
+ ws = EventMachine::WebSocketClient.connect('ws://127.0.0.1:12345/')
50
+ ws.errback { fail }
51
+ ws.stream {|msg|}
52
+ ws.callback {
53
+ ws.send_msg messages[0]
54
+ ws.send_msg messages[1]
60
55
  }
61
56
  end
62
57
 
63
- EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) do |ws|
58
+ start_server { |ws|
64
59
  ws.onopen {}
65
60
  ws.onclose {}
66
61
  ws.onmessage {|msg|
@@ -69,49 +64,60 @@ describe "WebSocket server draft75" do
69
64
 
70
65
  EventMachine.stop if received.size == messages.size
71
66
  }
72
- end
67
+ }
73
68
  }
74
69
  end
75
70
 
76
71
  it "should call onclose callback when client closes connection" do
77
72
  em {
78
73
  EventMachine.add_timer(0.1) do
79
- http = EventMachine::HttpRequest.new('ws://127.0.0.1:12345/').get :timeout => 0
80
- http.errback { fail }
81
- http.callback {
82
- http.response_header.status.should == 101
83
- http.close_connection
74
+ ws = EventMachine::WebSocketClient.connect('ws://127.0.0.1:12345/')
75
+ ws.errback { fail }
76
+ ws.callback {
77
+ ws.close_connection
84
78
  }
85
- http.stream{|msg|}
79
+ ws.stream{|msg|}
86
80
  end
87
81
 
88
- EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) do |ws|
82
+ start_server { |ws|
89
83
  ws.onopen {}
90
84
  ws.onclose {
91
85
  ws.state.should == :closed
92
86
  EventMachine.stop
93
87
  }
94
- end
88
+ }
95
89
  }
96
90
  end
97
91
 
98
92
  it "should call onerror callback with raised exception and close connection on bad handshake" do
99
93
  em {
100
94
  EventMachine.add_timer(0.1) do
101
- http = EventMachine::HttpRequest.new('http://127.0.0.1:12345/').get :timeout => 0
102
- http.errback { http.response_header.status.should == 0 }
95
+ http = EM::HttpRequest.new('http://127.0.0.1:12345/').get
96
+ http.errback { }
103
97
  http.callback { fail }
104
98
  end
105
99
 
106
- EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) do |ws|
100
+ start_server { |ws|
107
101
  ws.onopen { fail }
108
102
  ws.onclose { EventMachine.stop }
109
103
  ws.onerror {|e|
110
104
  e.should be_an_instance_of EventMachine::WebSocket::HandshakeError
111
- e.message.should match('Connection and Upgrade headers required')
105
+ e.message.should match('Not an upgrade request')
112
106
  EventMachine.stop
113
107
  }
114
- end
108
+ }
109
+ }
110
+ end
111
+
112
+ it "should report that close codes are not supported" do
113
+ em {
114
+ start_server { |ws|
115
+ ws.onopen {
116
+ ws.supports_close_codes?.should == false
117
+ done
118
+ }
119
+ }
120
+ start_client
115
121
  }
116
122
  end
117
123
  end
@@ -1,5 +1,6 @@
1
+ # encoding: BINARY
2
+
1
3
  require 'helper'
2
- require 'integration/shared_examples'
3
4
 
4
5
  describe "WebSocket server draft76" do
5
6
  include EM::SpecHelper
@@ -33,44 +34,37 @@ describe "WebSocket server draft76" do
33
34
  :body => "8jKS\'y:G*Co,Wxa-"
34
35
  }
35
36
  end
36
-
37
- it_behaves_like "a websocket server" do
38
- def start_server
39
- EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
40
- yield ws
41
- }
42
- end
43
37
 
44
- def start_client
45
- client = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
46
- client.send_data(format_request(@request))
47
- yield client if block_given?
48
- end
38
+ def start_client
39
+ client = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
40
+ client.send_data(format_request(@request))
41
+ yield client if block_given?
42
+ return client
43
+ end
44
+
45
+ it_behaves_like "a websocket server" do
46
+ let(:version) { 76 }
49
47
  end
50
48
 
51
49
  it "should send back the correct handshake response" do
52
50
  em {
53
- EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { }
54
-
55
- # Create a fake client which sends draft 76 handshake
56
- connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
57
- connection.send_data(format_request(@request))
51
+ start_server
58
52
 
59
- connection.onopen {
60
- connection.handshake_response.lines.sort.
61
- should == format_response(@response).lines.sort
62
- done
53
+ start_client { |connection|
54
+ connection.onopen {
55
+ connection.handshake_response.lines.sort.
56
+ should == format_response(@response).lines.sort
57
+ done
58
+ }
63
59
  }
64
60
  }
65
61
  end
66
62
 
67
63
  it "should send closing frame back and close the connection after recieving closing frame" do
68
64
  em {
69
- EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { }
65
+ start_server
70
66
 
71
- # Create a fake client which sends draft 76 handshake
72
- connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
73
- connection.send_data(format_request(@request))
67
+ connection = start_client
74
68
 
75
69
  # Send closing frame after handshake complete
76
70
  connection.onopen {
@@ -89,16 +83,14 @@ describe "WebSocket server draft76" do
89
83
 
90
84
  it "should ignore any data received after the closing frame" do
91
85
  em {
92
- EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
86
+ start_server { |ws|
93
87
  # Fail if foobar message is received
94
88
  ws.onmessage { |msg|
95
89
  fail
96
90
  }
97
91
  }
98
92
 
99
- # Create a fake client which sends draft 76 handshake
100
- connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
101
- connection.send_data(format_request(@request))
93
+ connection = start_client
102
94
 
103
95
  # Send closing frame after handshake complete, followed by another msg
104
96
  connection.onopen {
@@ -114,15 +106,13 @@ describe "WebSocket server draft76" do
114
106
 
115
107
  it "should accept null bytes within the frame after a line return" do
116
108
  em {
117
- EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
109
+ start_server { |ws|
118
110
  ws.onmessage { |msg|
119
111
  msg.should == "\n\000"
120
112
  }
121
113
  }
122
114
 
123
- # Create a fake client which sends draft 76 handshake
124
- connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
125
- connection.send_data(format_request(@request))
115
+ connection = start_client
126
116
 
127
117
  # Send closing frame after handshake complete
128
118
  connection.onopen {
@@ -138,7 +128,7 @@ describe "WebSocket server draft76" do
138
128
 
139
129
  it "should handle unreasonable frame lengths by calling onerror callback" do
140
130
  em {
141
- EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |server|
131
+ start_server { |server|
142
132
  server.onerror { |error|
143
133
  error.should be_an_instance_of EM::WebSocket::WSMessageTooBigError
144
134
  error.message.should == "Frame length too long (1180591620717411303296 bytes)"
@@ -146,9 +136,7 @@ describe "WebSocket server draft76" do
146
136
  }
147
137
  }
148
138
 
149
- # Create a fake client which sends draft 76 handshake
150
- client = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
151
- client.send_data(format_request(@request))
139
+ client = start_client
152
140
 
153
141
  # This particular frame indicates a message length of
154
142
  # 1180591620717411303296 bytes. Such a message would previously cause
@@ -162,7 +150,7 @@ describe "WebSocket server draft76" do
162
150
 
163
151
  it "should handle impossible frames by calling onerror callback" do
164
152
  em {
165
- EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |server|
153
+ start_server { |server|
166
154
  server.onerror { |error|
167
155
  error.should be_an_instance_of EM::WebSocket::WSProtocolError
168
156
  error.message.should == "Invalid frame received"
@@ -170,9 +158,7 @@ describe "WebSocket server draft76" do
170
158
  }
171
159
  }
172
160
 
173
- # Create a fake client which sends draft 76 handshake
174
- client = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
175
- client.send_data(format_request(@request))
161
+ client = start_client
176
162
 
177
163
  client.onopen {
178
164
  client.send_data("foobar") # Does not start with \x00 or \xff
@@ -182,10 +168,10 @@ describe "WebSocket server draft76" do
182
168
 
183
169
  it "should handle invalid http requests by raising HandshakeError passed to onerror callback" do
184
170
  em {
185
- EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |server|
171
+ start_server { |server|
186
172
  server.onerror { |error|
187
173
  error.should be_an_instance_of EM::WebSocket::HandshakeError
188
- error.message.should == "Invalid HTTP header"
174
+ error.message.should == "Invalid HTTP header: Could not parse data entirely (1 != 29)"
189
175
  done
190
176
  }
191
177
  }
@@ -197,7 +183,7 @@ describe "WebSocket server draft76" do
197
183
 
198
184
  it "should handle handshake request split into two TCP packets" do
199
185
  em {
200
- EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { }
186
+ start_server
201
187
 
202
188
  # Create a fake client which sends draft 76 handshake
203
189
  connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
@@ -217,4 +203,32 @@ describe "WebSocket server draft76" do
217
203
  end
218
204
  }
219
205
  end
206
+
207
+ it "should report that close codes are not supported" do
208
+ em {
209
+ start_server { |ws|
210
+ ws.onopen {
211
+ ws.supports_close_codes?.should == false
212
+ done
213
+ }
214
+ }
215
+ start_client
216
+ }
217
+ end
218
+
219
+ it "should call onclose when the server closes the connection [antiregression]" do
220
+ em {
221
+ start_server { |ws|
222
+ ws.onopen {
223
+ EM.add_timer(0.1) {
224
+ ws.close()
225
+ }
226
+ }
227
+ ws.onclose {
228
+ done
229
+ }
230
+ }
231
+ start_client
232
+ }
233
+ end
220
234
  end