em-websocket 0.3.7 → 0.5.2

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