websocket-rack 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,212 @@
1
+ # require 'helper'
2
+ #
3
+ # describe "WebSocket server draft76" 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
+ #
21
+ # @response = {
22
+ # :headers => {
23
+ # "Upgrade" => "WebSocket",
24
+ # "Connection" => "Upgrade",
25
+ # "Sec-WebSocket-Location" => "ws://example.com/demo",
26
+ # "Sec-WebSocket-Origin" => "http://example.com",
27
+ # "Sec-WebSocket-Protocol" => "sample"
28
+ # },
29
+ # :body => "8jKS\'y:G*Co,Wxa-"
30
+ # }
31
+ # end
32
+ #
33
+ # it "should send back the correct handshake response" do
34
+ # EM.run do
35
+ # EM.add_timer(0.1) do
36
+ # EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { }
37
+ #
38
+ # # Create a fake client which sends draft 76 handshake
39
+ # connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
40
+ # connection.send_data(format_request(@request))
41
+ #
42
+ # connection.onopen = lambda {
43
+ # connection.handshake_response.lines.sort.
44
+ # should == format_response(@response).lines.sort
45
+ # EM.stop
46
+ # }
47
+ # end
48
+ # end
49
+ # end
50
+ #
51
+ # it "should send closing frame back and close the connection after recieving closing frame" do
52
+ # EM.run do
53
+ # EM.add_timer(0.1) do
54
+ # EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { }
55
+ #
56
+ # # Create a fake client which sends draft 76 handshake
57
+ # connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
58
+ # connection.send_data(format_request(@request))
59
+ #
60
+ # # Send closing frame after handshake complete
61
+ # connection.onopen = lambda {
62
+ # connection.send_data(EM::WebSocket::Handler76::TERMINATE_STRING)
63
+ # }
64
+ #
65
+ # # Check that this causes a termination string to be returned and the
66
+ # # connection close
67
+ # connection.onclose = lambda {
68
+ # connection.packets[0].should ==
69
+ # EM::WebSocket::Handler76::TERMINATE_STRING
70
+ # EM.stop
71
+ # }
72
+ # end
73
+ # end
74
+ # end
75
+ #
76
+ # it "should ignore any data received after the closing frame" do
77
+ # EM.run do
78
+ # EM.add_timer(0.1) do
79
+ # EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
80
+ # # Fail if foobar message is received
81
+ # ws.onmessage { |msg|
82
+ # failed
83
+ # }
84
+ # }
85
+ #
86
+ # # Create a fake client which sends draft 76 handshake
87
+ # connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
88
+ # connection.send_data(format_request(@request))
89
+ #
90
+ # # Send closing frame after handshake complete, followed by another msg
91
+ # connection.onopen = lambda {
92
+ # connection.send_data(EM::WebSocket::Handler76::TERMINATE_STRING)
93
+ # connection.send('foobar')
94
+ # }
95
+ #
96
+ # connection.onclose = lambda {
97
+ # EM.stop
98
+ # }
99
+ # end
100
+ # end
101
+ # end
102
+ #
103
+ # it "should accept null bytes within the frame after a line return" do
104
+ # EM.run do
105
+ # EM.add_timer(0.1) do
106
+ # EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
107
+ # ws.onmessage { |msg|
108
+ # msg.should == "\n\000"
109
+ # }
110
+ # }
111
+ #
112
+ # # Create a fake client which sends draft 76 handshake
113
+ # connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
114
+ # connection.send_data(format_request(@request))
115
+ #
116
+ # # Send closing frame after handshake complete
117
+ # connection.onopen = lambda {
118
+ # connection.send_data("\000\n\000\377")
119
+ # connection.send_data(EM::WebSocket::Handler76::TERMINATE_STRING)
120
+ # }
121
+ #
122
+ # connection.onclose = lambda {
123
+ # EM.stop
124
+ # }
125
+ # end
126
+ # end
127
+ # end
128
+ #
129
+ # it "should handle unreasonable frame lengths by calling onerror callback" do
130
+ # EM.run do
131
+ # EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |server|
132
+ # server.onerror { |error|
133
+ # error.should be_an_instance_of EM::WebSocket::DataError
134
+ # error.message.should == "Frame length too long (1180591620717411303296 bytes)"
135
+ # EM.stop
136
+ # }
137
+ # }
138
+ #
139
+ # # Create a fake client which sends draft 76 handshake
140
+ # client = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
141
+ # client.send_data(format_request(@request))
142
+ #
143
+ # # This particular frame indicates a message length of
144
+ # # 1180591620717411303296 bytes. Such a message would previously cause
145
+ # # a "bignum too big to convert into `long'" error.
146
+ # # However it is clearly unreasonable and should be rejected.
147
+ # client.onopen = lambda {
148
+ # client.send_data("\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00")
149
+ # }
150
+ # end
151
+ # end
152
+ #
153
+ # it "should handle impossible frames by calling onerror callback" do
154
+ # EM.run do
155
+ # EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |server|
156
+ # server.onerror { |error|
157
+ # error.should be_an_instance_of EM::WebSocket::DataError
158
+ # error.message.should == "Invalid frame received"
159
+ # EM.stop
160
+ # }
161
+ # }
162
+ #
163
+ # # Create a fake client which sends draft 76 handshake
164
+ # client = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
165
+ # client.send_data(format_request(@request))
166
+ #
167
+ # client.onopen = lambda {
168
+ # client.send_data("foobar") # Does not start with \x00 or \xff
169
+ # }
170
+ # end
171
+ # end
172
+ #
173
+ # it "should handle invalid http requests by raising HandshakeError passed to onerror callback" do
174
+ # EM.run {
175
+ # EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |server|
176
+ # server.onerror { |error|
177
+ # error.should be_an_instance_of EM::WebSocket::HandshakeError
178
+ # error.message.should == "Invalid HTTP header"
179
+ # EM.stop
180
+ # }
181
+ # }
182
+ #
183
+ # client = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
184
+ # client.send_data("This is not a HTTP header\r\n\r\n")
185
+ # }
186
+ # end
187
+ #
188
+ # it "should handle handshake request split into two TCP packets" do
189
+ # EM.run do
190
+ # EM.add_timer(0.1) do
191
+ # EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { }
192
+ #
193
+ # # Create a fake client which sends draft 76 handshake
194
+ # connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
195
+ # data = format_request(@request)
196
+ # # Sends first half of the request
197
+ # connection.send_data(data[0...(data.length / 2)])
198
+ #
199
+ # connection.onopen = lambda {
200
+ # connection.handshake_response.lines.sort.
201
+ # should == format_response(@response).lines.sort
202
+ # EM.stop
203
+ # }
204
+ #
205
+ # EM.add_timer(0.1) do
206
+ # # Sends second half of the request
207
+ # connection.send_data(data[(data.length / 2)..-1])
208
+ # end
209
+ # end
210
+ # end
211
+ # end
212
+ # end
@@ -0,0 +1,108 @@
1
+ require 'helper'
2
+
3
+ describe Rack::WebSocket::Framing03 do
4
+ class FramingContainer
5
+ include Rack::WebSocket::Framing03
6
+
7
+ def <<(data)
8
+ @data << data
9
+ process_data(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(Rack::WebSocket::WebSocketError, 'Continuation frame not expected')
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,136 @@
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