websocket-rack 0.1.4 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +11 -0
- data/README.md +42 -24
- data/Rakefile +1 -1
- data/example/example.ru +5 -5
- data/lib/rack/websocket/application.rb +31 -60
- data/lib/rack/websocket/extensions/common.rb +61 -0
- data/lib/rack/websocket/extensions/thin/connection.rb +2 -50
- data/lib/rack/websocket/extensions/thin.rb +3 -3
- data/lib/rack/websocket/extensions.rb +14 -0
- data/lib/rack/websocket/handler/base.rb +41 -0
- data/lib/rack/websocket/handler/stub.rb +14 -0
- data/lib/rack/websocket/handler/thin/connection.rb +89 -0
- data/lib/rack/websocket/handler/thin/handler_factory.rb +56 -0
- data/lib/rack/websocket/handler/thin.rb +61 -0
- data/lib/rack/websocket/handler.rb +15 -38
- data/lib/rack/websocket/version.rb +5 -0
- data/lib/rack/websocket.rb +5 -31
- data/spec/spec_helper.rb +18 -0
- data/spec/support/all_drafts.rb +43 -0
- data/spec/support/all_handlers.rb +31 -0
- data/spec/support/requests.rb +100 -0
- data/spec/thin_spec.rb +46 -0
- data/websocket-rack.gemspec +4 -4
- metadata +41 -47
- data/lib/rack/websocket/connection.rb +0 -112
- data/lib/rack/websocket/debugger.rb +0 -17
- data/lib/rack/websocket/framing03.rb +0 -178
- data/lib/rack/websocket/framing76.rb +0 -115
- data/lib/rack/websocket/handler03.rb +0 -14
- data/lib/rack/websocket/handler75.rb +0 -8
- data/lib/rack/websocket/handler76.rb +0 -11
- data/lib/rack/websocket/handler_factory.rb +0 -61
- data/lib/rack/websocket/handshake75.rb +0 -21
- data/lib/rack/websocket/handshake76.rb +0 -71
- data/spec/helper.rb +0 -44
- data/spec/integration/draft03_spec.rb +0 -252
- data/spec/integration/draft76_spec.rb +0 -212
- data/spec/unit/framing_spec.rb +0 -108
- data/spec/unit/handler_spec.rb +0 -136
- data/spec/websocket_spec.rb +0 -210
data/spec/unit/handler_spec.rb
DELETED
@@ -1,136 +0,0 @@
|
|
1
|
-
require 'helper'
|
2
|
-
|
3
|
-
describe "Rack::WebSocket::Handler" 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
|
-
},
|
18
|
-
:body => '^n:ds[4U'
|
19
|
-
}
|
20
|
-
@secure_request = @request.merge(:port => 443)
|
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
|
-
@secure_response = @response.merge(:headers => @response[:headers].merge('Sec-WebSocket-Location' => "wss://example.com:443/demo"))
|
33
|
-
end
|
34
|
-
|
35
|
-
it "should handle good request" do
|
36
|
-
handler(@request).should send_handshake(@response)
|
37
|
-
end
|
38
|
-
|
39
|
-
it "should handle good request to secure default port if secure mode is enabled" do
|
40
|
-
handler(@secure_request, true).should send_handshake(@secure_response)
|
41
|
-
end
|
42
|
-
|
43
|
-
it "should not handle good request to secure default port if secure mode is disabled" do
|
44
|
-
handler(@secure_request, false).should_not send_handshake(@secure_response)
|
45
|
-
end
|
46
|
-
|
47
|
-
it "should handle good request on nondefault port" do
|
48
|
-
@request[:port] = 8081
|
49
|
-
@request[:headers]['Host'] = 'example.com:8081'
|
50
|
-
@response[:headers]['Sec-WebSocket-Location'] =
|
51
|
-
'ws://example.com:8081/demo'
|
52
|
-
|
53
|
-
handler(@request).should send_handshake(@response)
|
54
|
-
end
|
55
|
-
|
56
|
-
it "should handle good request to secure nondefault port" do
|
57
|
-
@secure_request[:port] = 8081
|
58
|
-
@secure_request[:headers]['Host'] = 'example.com:8081'
|
59
|
-
@secure_response[:headers]['Sec-WebSocket-Location'] = 'wss://example.com:8081/demo'
|
60
|
-
handler(@secure_request, true).should send_handshake(@secure_response)
|
61
|
-
end
|
62
|
-
|
63
|
-
it "should handle good request with no protocol" do
|
64
|
-
@request[:headers].delete('Sec-WebSocket-Protocol')
|
65
|
-
@response[:headers].delete("Sec-WebSocket-Protocol")
|
66
|
-
|
67
|
-
handler(@request).should send_handshake(@response)
|
68
|
-
end
|
69
|
-
|
70
|
-
it "should handle extra headers by simply ignoring them" do
|
71
|
-
@request[:headers]['EmptyValue'] = ""
|
72
|
-
@request[:headers]['AKey'] = "AValue"
|
73
|
-
|
74
|
-
handler(@request).should send_handshake(@response)
|
75
|
-
end
|
76
|
-
|
77
|
-
it "should raise error on HTTP request" do
|
78
|
-
@request[:headers] = {
|
79
|
-
'Host' => 'www.google.com',
|
80
|
-
'User-Agent' => 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 GTB6 GTBA',
|
81
|
-
'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
82
|
-
'Accept-Language' => 'en-us,en;q=0.5',
|
83
|
-
'Accept-Encoding' => 'gzip,deflate',
|
84
|
-
'Accept-Charset' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
|
85
|
-
'Keep-Alive' => '300',
|
86
|
-
'Connection' => 'keep-alive',
|
87
|
-
}
|
88
|
-
|
89
|
-
lambda {
|
90
|
-
handler(@request).handshake
|
91
|
-
}.should raise_error(Rack::WebSocket::HandshakeError)
|
92
|
-
end
|
93
|
-
|
94
|
-
it "should raise error on wrong method" do
|
95
|
-
@request[:method] = 'POST'
|
96
|
-
|
97
|
-
lambda {
|
98
|
-
handler(@request).handshake
|
99
|
-
}.should raise_error(Rack::WebSocket::HandshakeError)
|
100
|
-
end
|
101
|
-
|
102
|
-
it "should raise error if upgrade header incorrect" do
|
103
|
-
@request[:headers]['Upgrade'] = 'NonWebSocket'
|
104
|
-
|
105
|
-
lambda {
|
106
|
-
handler(@request).handshake
|
107
|
-
}.should raise_error(Rack::WebSocket::HandshakeError)
|
108
|
-
end
|
109
|
-
|
110
|
-
it "should raise error if Sec-WebSocket-Protocol is empty" do
|
111
|
-
@request[:headers]['Sec-WebSocket-Protocol'] = ''
|
112
|
-
|
113
|
-
lambda {
|
114
|
-
handler(@request).handshake
|
115
|
-
}.should raise_error(Rack::WebSocket::HandshakeError)
|
116
|
-
end
|
117
|
-
|
118
|
-
%w[Sec-WebSocket-Key1 Sec-WebSocket-Key2].each do |header|
|
119
|
-
it "should raise error if #{header} has zero spaces" do
|
120
|
-
@request[:headers][header] = 'nospaces'
|
121
|
-
|
122
|
-
lambda {
|
123
|
-
handler(@request).handshake
|
124
|
-
}.should raise_error(Rack::WebSocket::HandshakeError, 'Websocket Key1 or Key2 does not contain spaces - this is a symptom of a cross-protocol attack')
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
it "should raise error if spaces do not divide numbers in Sec-WebSocket-Key* " do
|
129
|
-
@request[:headers]['Sec-WebSocket-Key2'] = '12998 5 Y3 1.P00'
|
130
|
-
|
131
|
-
lambda {
|
132
|
-
handler(@request).handshake
|
133
|
-
}.should raise_error(Rack::WebSocket::HandshakeError, 'Invalid Key "12998 5 Y3 1.P00"')
|
134
|
-
end
|
135
|
-
|
136
|
-
end
|
data/spec/websocket_spec.rb
DELETED
@@ -1,210 +0,0 @@
|
|
1
|
-
# require 'helper'
|
2
|
-
#
|
3
|
-
# describe Rack::WebSocket do
|
4
|
-
#
|
5
|
-
# it "should automatically complete WebSocket handshake" do
|
6
|
-
# EM.run do
|
7
|
-
# MSG = "Hello World!"
|
8
|
-
# EventMachine.add_timer(0.1) do
|
9
|
-
# http = EventMachine::HttpRequest.new('ws://127.0.0.1:12345/').get :timeout => 0
|
10
|
-
# http.errback { failed }
|
11
|
-
# http.callback { http.response_header.status.should == 101 }
|
12
|
-
#
|
13
|
-
# http.stream { |msg|
|
14
|
-
# msg.should == MSG
|
15
|
-
# EventMachine.stop
|
16
|
-
# }
|
17
|
-
# end
|
18
|
-
#
|
19
|
-
# EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) do |ws|
|
20
|
-
# ws.onopen {
|
21
|
-
# ws.send MSG
|
22
|
-
# }
|
23
|
-
# end
|
24
|
-
# end
|
25
|
-
# end
|
26
|
-
#
|
27
|
-
# it "should fail on non WebSocket requests" do
|
28
|
-
# EM.run do
|
29
|
-
# EventMachine.add_timer(0.1) do
|
30
|
-
# http = EventMachine::HttpRequest.new('http://127.0.0.1:12345/').get :timeout => 0
|
31
|
-
# http.errback { failed }
|
32
|
-
# http.callback {
|
33
|
-
# http.response_header.status.should == 400
|
34
|
-
# EventMachine.stop
|
35
|
-
# }
|
36
|
-
# end
|
37
|
-
#
|
38
|
-
# EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) {}
|
39
|
-
# end
|
40
|
-
# end
|
41
|
-
#
|
42
|
-
# it "should split multiple messages into separate callbacks" do
|
43
|
-
# EM.run do
|
44
|
-
# messages = %w[1 2]
|
45
|
-
# received = []
|
46
|
-
#
|
47
|
-
# EventMachine.add_timer(0.1) do
|
48
|
-
# http = EventMachine::HttpRequest.new('ws://127.0.0.1:12345/').get :timeout => 0
|
49
|
-
# http.errback { failed }
|
50
|
-
# http.stream {|msg|}
|
51
|
-
# http.callback {
|
52
|
-
# http.response_header.status.should == 101
|
53
|
-
# http.send messages[0]
|
54
|
-
# http.send messages[1]
|
55
|
-
# }
|
56
|
-
# end
|
57
|
-
#
|
58
|
-
# EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) do |ws|
|
59
|
-
# ws.onopen {}
|
60
|
-
# ws.onclose {}
|
61
|
-
# ws.onmessage {|msg|
|
62
|
-
# msg.should == messages[received.size]
|
63
|
-
# received.push msg
|
64
|
-
#
|
65
|
-
# EventMachine.stop if received.size == messages.size
|
66
|
-
# }
|
67
|
-
# end
|
68
|
-
# end
|
69
|
-
# end
|
70
|
-
#
|
71
|
-
# it "should call onclose callback when client closes connection" do
|
72
|
-
# EM.run do
|
73
|
-
# EventMachine.add_timer(0.1) do
|
74
|
-
# http = EventMachine::HttpRequest.new('ws://127.0.0.1:12345/').get :timeout => 0
|
75
|
-
# http.errback { failed }
|
76
|
-
# http.callback {
|
77
|
-
# http.response_header.status.should == 101
|
78
|
-
# http.close_connection
|
79
|
-
# }
|
80
|
-
# http.stream{|msg|}
|
81
|
-
# end
|
82
|
-
#
|
83
|
-
# EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) do |ws|
|
84
|
-
# ws.onopen {}
|
85
|
-
# ws.onclose {
|
86
|
-
# ws.state.should == :closed
|
87
|
-
# EventMachine.stop
|
88
|
-
# }
|
89
|
-
# end
|
90
|
-
# end
|
91
|
-
# end
|
92
|
-
#
|
93
|
-
# it "should call onerror callback with raised exception and close connection on bad handshake" do
|
94
|
-
# EM.run do
|
95
|
-
# EventMachine.add_timer(0.1) do
|
96
|
-
# http = EventMachine::HttpRequest.new('http://127.0.0.1:12345/').get :timeout => 0
|
97
|
-
# http.errback { http.response_header.status.should == 0 }
|
98
|
-
# http.callback { failed }
|
99
|
-
# end
|
100
|
-
#
|
101
|
-
# EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) do |ws|
|
102
|
-
# ws.onopen { failed }
|
103
|
-
# ws.onclose { EventMachine.stop }
|
104
|
-
# ws.onerror {|e|
|
105
|
-
# e.should be_an_instance_of EventMachine::WebSocket::HandshakeError
|
106
|
-
# e.message.should match('Connection and Upgrade headers required')
|
107
|
-
# EventMachine.stop
|
108
|
-
# }
|
109
|
-
# end
|
110
|
-
# end
|
111
|
-
# end
|
112
|
-
#
|
113
|
-
# it "should populate ws.request with appropriate headers" do
|
114
|
-
# EM.run do
|
115
|
-
# EventMachine.add_timer(0.1) do
|
116
|
-
# http = EventMachine::HttpRequest.new('ws://127.0.0.1:12345/').get :timeout => 0
|
117
|
-
# http.errback { failed }
|
118
|
-
# http.callback {
|
119
|
-
# http.response_header.status.should == 101
|
120
|
-
# http.close_connection
|
121
|
-
# }
|
122
|
-
# http.stream { |msg| }
|
123
|
-
# end
|
124
|
-
#
|
125
|
-
# EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) do |ws|
|
126
|
-
# ws.onopen {
|
127
|
-
# ws.request["User-Agent"].should == "EventMachine HttpClient"
|
128
|
-
# ws.request["Connection"].should == "Upgrade"
|
129
|
-
# ws.request["Upgrade"].should == "WebSocket"
|
130
|
-
# ws.request["Path"].should == "/"
|
131
|
-
# ws.request["Origin"].should == "127.0.0.1"
|
132
|
-
# ws.request["Host"].to_s.should == "ws://127.0.0.1:12345"
|
133
|
-
# }
|
134
|
-
# ws.onclose {
|
135
|
-
# ws.state.should == :closed
|
136
|
-
# EventMachine.stop
|
137
|
-
# }
|
138
|
-
# end
|
139
|
-
# end
|
140
|
-
# end
|
141
|
-
#
|
142
|
-
# it "should allow sending and retrieving query string args passed in on the connection request." do
|
143
|
-
# EM.run do
|
144
|
-
# EventMachine.add_timer(0.1) do
|
145
|
-
# http = EventMachine::HttpRequest.new('ws://127.0.0.1:12345/').get(:query => {'foo' => 'bar', 'baz' => 'qux'}, :timeout => 0)
|
146
|
-
# http.errback { failed }
|
147
|
-
# http.callback {
|
148
|
-
# http.response_header.status.should == 101
|
149
|
-
# http.close_connection
|
150
|
-
# }
|
151
|
-
# http.stream { |msg| }
|
152
|
-
# end
|
153
|
-
#
|
154
|
-
# EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) do |ws|
|
155
|
-
# ws.onopen {
|
156
|
-
# path, query = ws.request["Path"].split('?')
|
157
|
-
# path.should == '/'
|
158
|
-
# Hash[*query.split(/&|=/)].should == {"foo"=>"bar", "baz"=>"qux"}
|
159
|
-
# ws.request["Query"]["foo"].should == "bar"
|
160
|
-
# ws.request["Query"]["baz"].should == "qux"
|
161
|
-
# }
|
162
|
-
# ws.onclose {
|
163
|
-
# ws.state.should == :closed
|
164
|
-
# EventMachine.stop
|
165
|
-
# }
|
166
|
-
# end
|
167
|
-
# end
|
168
|
-
# end
|
169
|
-
#
|
170
|
-
# it "should ws.response['Query'] to empty hash when no query string params passed in connection URI" do
|
171
|
-
# EM.run do
|
172
|
-
# EventMachine.add_timer(0.1) do
|
173
|
-
# http = EventMachine::HttpRequest.new('ws://127.0.0.1:12345/').get(:timeout => 0)
|
174
|
-
# http.errback { failed }
|
175
|
-
# http.callback {
|
176
|
-
# http.response_header.status.should == 101
|
177
|
-
# http.close_connection
|
178
|
-
# }
|
179
|
-
# http.stream { |msg| }
|
180
|
-
# end
|
181
|
-
#
|
182
|
-
# EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) do |ws|
|
183
|
-
# ws.onopen {
|
184
|
-
# ws.request["Path"].should == "/"
|
185
|
-
# ws.request["Query"].should == {}
|
186
|
-
# }
|
187
|
-
# ws.onclose {
|
188
|
-
# ws.state.should == :closed
|
189
|
-
# EventMachine.stop
|
190
|
-
# }
|
191
|
-
# end
|
192
|
-
# end
|
193
|
-
# end
|
194
|
-
#
|
195
|
-
# it "should raise an exception if frame sent before handshake complete" do
|
196
|
-
# EM.run {
|
197
|
-
# EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |c|
|
198
|
-
# # We're not using a real client so the handshake will not be sent
|
199
|
-
# EM.add_timer(0.1) {
|
200
|
-
# lambda {
|
201
|
-
# c.send('early message')
|
202
|
-
# }.should raise_error('Cannot send data before onopen callback')
|
203
|
-
# EM.stop
|
204
|
-
# }
|
205
|
-
# }
|
206
|
-
#
|
207
|
-
# client = EM.connect('0.0.0.0', 12345, EM::Connection)
|
208
|
-
# }
|
209
|
-
# end
|
210
|
-
# end
|