rack 1.6.13 → 2.1.4.3
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rack might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +92 -0
- data/{COPYING → MIT-LICENSE} +4 -2
- data/README.rdoc +105 -141
- data/Rakefile +27 -28
- data/SPEC +6 -7
- data/bin/rackup +1 -0
- data/contrib/rack_logo.svg +164 -111
- data/example/lobster.ru +2 -0
- data/example/protectedlobster.rb +4 -2
- data/example/protectedlobster.ru +3 -1
- data/lib/rack/auth/abstract/handler.rb +3 -1
- data/lib/rack/auth/abstract/request.rb +7 -1
- data/lib/rack/auth/basic.rb +4 -1
- data/lib/rack/auth/digest/md5.rb +9 -7
- data/lib/rack/auth/digest/nonce.rb +6 -3
- data/lib/rack/auth/digest/params.rb +5 -4
- data/lib/rack/auth/digest/request.rb +3 -1
- data/lib/rack/body_proxy.rb +11 -9
- data/lib/rack/builder.rb +42 -18
- data/lib/rack/cascade.rb +6 -5
- data/lib/rack/chunked.rb +33 -10
- data/lib/rack/{commonlogger.rb → common_logger.rb} +14 -10
- data/lib/rack/{conditionalget.rb → conditional_get.rb} +3 -1
- data/lib/rack/config.rb +2 -0
- data/lib/rack/content_length.rb +5 -3
- data/lib/rack/content_type.rb +3 -1
- data/lib/rack/core_ext/regexp.rb +14 -0
- data/lib/rack/deflater.rb +33 -53
- data/lib/rack/directory.rb +75 -60
- data/lib/rack/etag.rb +8 -5
- data/lib/rack/events.rb +156 -0
- data/lib/rack/file.rb +4 -149
- data/lib/rack/files.rb +178 -0
- data/lib/rack/handler/cgi.rb +18 -17
- data/lib/rack/handler/fastcgi.rb +17 -16
- data/lib/rack/handler/lsws.rb +14 -12
- data/lib/rack/handler/scgi.rb +22 -19
- data/lib/rack/handler/thin.rb +6 -1
- data/lib/rack/handler/webrick.rb +28 -28
- data/lib/rack/handler.rb +9 -26
- data/lib/rack/head.rb +17 -17
- data/lib/rack/lint.rb +55 -52
- data/lib/rack/lobster.rb +8 -6
- data/lib/rack/lock.rb +17 -10
- data/lib/rack/logger.rb +4 -2
- data/lib/rack/media_type.rb +43 -0
- data/lib/rack/{methodoverride.rb → method_override.rb} +10 -8
- data/lib/rack/mime.rb +27 -6
- data/lib/rack/mock.rb +101 -60
- data/lib/rack/multipart/generator.rb +11 -12
- data/lib/rack/multipart/parser.rb +292 -161
- data/lib/rack/multipart/uploaded_file.rb +3 -2
- data/lib/rack/multipart.rb +38 -8
- data/lib/rack/{nulllogger.rb → null_logger.rb} +3 -1
- data/lib/rack/query_parser.rb +218 -0
- data/lib/rack/recursive.rb +11 -9
- data/lib/rack/reloader.rb +10 -4
- data/lib/rack/request.rb +447 -305
- data/lib/rack/response.rb +196 -83
- data/lib/rack/rewindable_input.rb +5 -14
- data/lib/rack/runtime.rb +12 -18
- data/lib/rack/sendfile.rb +19 -14
- data/lib/rack/server.rb +118 -41
- data/lib/rack/session/abstract/id.rb +139 -94
- data/lib/rack/session/cookie.rb +34 -26
- data/lib/rack/session/memcache.rb +4 -93
- data/lib/rack/session/pool.rb +12 -10
- data/lib/rack/show_exceptions.rb +392 -0
- data/lib/rack/{showstatus.rb → show_status.rb} +7 -5
- data/lib/rack/static.rb +41 -11
- data/lib/rack/tempfile_reaper.rb +4 -2
- data/lib/rack/urlmap.rb +25 -15
- data/lib/rack/utils.rb +203 -277
- data/lib/rack.rb +76 -24
- data/rack.gemspec +25 -14
- metadata +62 -183
- data/HISTORY.md +0 -375
- data/KNOWN-ISSUES +0 -44
- data/lib/rack/backports/uri/common_18.rb +0 -56
- data/lib/rack/backports/uri/common_192.rb +0 -52
- data/lib/rack/backports/uri/common_193.rb +0 -29
- data/lib/rack/handler/evented_mongrel.rb +0 -8
- data/lib/rack/handler/mongrel.rb +0 -106
- data/lib/rack/handler/swiftiplied_mongrel.rb +0 -8
- data/lib/rack/showexceptions.rb +0 -387
- data/lib/rack/utils/okjson.rb +0 -600
- data/test/builder/anything.rb +0 -5
- data/test/builder/comment.ru +0 -4
- data/test/builder/end.ru +0 -5
- data/test/builder/line.ru +0 -1
- data/test/builder/options.ru +0 -2
- data/test/cgi/assets/folder/test.js +0 -1
- data/test/cgi/assets/fonts/font.eot +0 -1
- data/test/cgi/assets/images/image.png +0 -1
- data/test/cgi/assets/index.html +0 -1
- data/test/cgi/assets/javascripts/app.js +0 -1
- data/test/cgi/assets/stylesheets/app.css +0 -1
- data/test/cgi/lighttpd.conf +0 -26
- data/test/cgi/rackup_stub.rb +0 -6
- data/test/cgi/sample_rackup.ru +0 -5
- data/test/cgi/test +0 -9
- data/test/cgi/test+directory/test+file +0 -1
- data/test/cgi/test.fcgi +0 -8
- data/test/cgi/test.ru +0 -5
- data/test/gemloader.rb +0 -10
- data/test/multipart/bad_robots +0 -259
- data/test/multipart/binary +0 -0
- data/test/multipart/content_type_and_no_filename +0 -6
- data/test/multipart/empty +0 -10
- data/test/multipart/fail_16384_nofile +0 -814
- data/test/multipart/file1.txt +0 -1
- data/test/multipart/filename_and_modification_param +0 -7
- data/test/multipart/filename_and_no_name +0 -6
- data/test/multipart/filename_with_escaped_quotes +0 -6
- data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
- data/test/multipart/filename_with_null_byte +0 -7
- data/test/multipart/filename_with_percent_escaped_quotes +0 -6
- data/test/multipart/filename_with_unescaped_percentages +0 -6
- data/test/multipart/filename_with_unescaped_percentages2 +0 -6
- data/test/multipart/filename_with_unescaped_percentages3 +0 -6
- data/test/multipart/filename_with_unescaped_quotes +0 -6
- data/test/multipart/ie +0 -6
- data/test/multipart/invalid_character +0 -6
- data/test/multipart/mixed_files +0 -21
- data/test/multipart/nested +0 -10
- data/test/multipart/none +0 -9
- data/test/multipart/semicolon +0 -6
- data/test/multipart/text +0 -15
- data/test/multipart/three_files_three_fields +0 -31
- data/test/multipart/webkit +0 -32
- data/test/rackup/config.ru +0 -31
- data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
- data/test/spec_auth_basic.rb +0 -81
- data/test/spec_auth_digest.rb +0 -259
- data/test/spec_body_proxy.rb +0 -85
- data/test/spec_builder.rb +0 -223
- data/test/spec_cascade.rb +0 -61
- data/test/spec_cgi.rb +0 -102
- data/test/spec_chunked.rb +0 -101
- data/test/spec_commonlogger.rb +0 -93
- data/test/spec_conditionalget.rb +0 -102
- data/test/spec_config.rb +0 -22
- data/test/spec_content_length.rb +0 -85
- data/test/spec_content_type.rb +0 -45
- data/test/spec_deflater.rb +0 -339
- data/test/spec_directory.rb +0 -88
- data/test/spec_etag.rb +0 -107
- data/test/spec_fastcgi.rb +0 -107
- data/test/spec_file.rb +0 -221
- data/test/spec_handler.rb +0 -72
- data/test/spec_head.rb +0 -45
- data/test/spec_lint.rb +0 -550
- data/test/spec_lobster.rb +0 -58
- data/test/spec_lock.rb +0 -164
- data/test/spec_logger.rb +0 -23
- data/test/spec_methodoverride.rb +0 -111
- data/test/spec_mime.rb +0 -51
- data/test/spec_mock.rb +0 -297
- data/test/spec_mongrel.rb +0 -182
- data/test/spec_multipart.rb +0 -600
- data/test/spec_nulllogger.rb +0 -20
- data/test/spec_recursive.rb +0 -72
- data/test/spec_request.rb +0 -1232
- data/test/spec_response.rb +0 -407
- data/test/spec_rewindable_input.rb +0 -118
- data/test/spec_runtime.rb +0 -49
- data/test/spec_sendfile.rb +0 -130
- data/test/spec_server.rb +0 -167
- data/test/spec_session_abstract_id.rb +0 -53
- data/test/spec_session_cookie.rb +0 -410
- data/test/spec_session_memcache.rb +0 -358
- data/test/spec_session_persisted_secure_secure_session_hash.rb +0 -73
- data/test/spec_session_pool.rb +0 -246
- data/test/spec_showexceptions.rb +0 -98
- data/test/spec_showstatus.rb +0 -103
- data/test/spec_static.rb +0 -145
- data/test/spec_tempfile_reaper.rb +0 -63
- data/test/spec_thin.rb +0 -91
- data/test/spec_urlmap.rb +0 -236
- data/test/spec_utils.rb +0 -647
- data/test/spec_version.rb +0 -17
- data/test/spec_webrick.rb +0 -184
- data/test/static/another/index.html +0 -1
- data/test/static/index.html +0 -1
- data/test/testrequest.rb +0 -78
- data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
- data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
data/test/spec_server.rb
DELETED
@@ -1,167 +0,0 @@
|
|
1
|
-
require 'rack'
|
2
|
-
require 'rack/server'
|
3
|
-
require 'tempfile'
|
4
|
-
require 'socket'
|
5
|
-
require 'open-uri'
|
6
|
-
|
7
|
-
describe Rack::Server do
|
8
|
-
|
9
|
-
def app
|
10
|
-
lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['success']] }
|
11
|
-
end
|
12
|
-
|
13
|
-
def with_stderr
|
14
|
-
old, $stderr = $stderr, StringIO.new
|
15
|
-
yield $stderr
|
16
|
-
ensure
|
17
|
-
$stderr = old
|
18
|
-
end
|
19
|
-
|
20
|
-
it "overrides :config if :app is passed in" do
|
21
|
-
server = Rack::Server.new(:app => "FOO")
|
22
|
-
server.app.should.equal "FOO"
|
23
|
-
end
|
24
|
-
|
25
|
-
should "prefer to use :builder when it is passed in" do
|
26
|
-
server = Rack::Server.new(:builder => "run lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['success']] }")
|
27
|
-
server.app.class.should.equal Proc
|
28
|
-
Rack::MockRequest.new(server.app).get("/").body.to_s.should.equal 'success'
|
29
|
-
end
|
30
|
-
|
31
|
-
should "allow subclasses to override middleware" do
|
32
|
-
server = Class.new(Rack::Server).class_eval { def middleware; Hash.new [] end; self }
|
33
|
-
server.middleware['deployment'].should.not.equal []
|
34
|
-
server.new(:app => 'foo').middleware['deployment'].should.equal []
|
35
|
-
end
|
36
|
-
|
37
|
-
should "allow subclasses to override default middleware" do
|
38
|
-
server = Class.new(Rack::Server).instance_eval { def default_middleware_by_environment; Hash.new [] end; self }
|
39
|
-
server.middleware['deployment'].should.equal []
|
40
|
-
server.new(:app => 'foo').middleware['deployment'].should.equal []
|
41
|
-
end
|
42
|
-
|
43
|
-
should "only provide default middleware for development and deployment environments" do
|
44
|
-
Rack::Server.default_middleware_by_environment.keys.sort.should.equal %w(deployment development)
|
45
|
-
end
|
46
|
-
|
47
|
-
should "always return an empty array for unknown environments" do
|
48
|
-
server = Rack::Server.new(:app => 'foo')
|
49
|
-
server.middleware['production'].should.equal []
|
50
|
-
end
|
51
|
-
|
52
|
-
should "not include Rack::Lint in deployment environment" do
|
53
|
-
server = Rack::Server.new(:app => 'foo')
|
54
|
-
server.middleware['deployment'].flatten.should.not.include(Rack::Lint)
|
55
|
-
end
|
56
|
-
|
57
|
-
should "not include Rack::ShowExceptions in deployment environment" do
|
58
|
-
server = Rack::Server.new(:app => 'foo')
|
59
|
-
server.middleware['deployment'].flatten.should.not.include(Rack::ShowExceptions)
|
60
|
-
end
|
61
|
-
|
62
|
-
should "include Rack::TempfileReaper in deployment environment" do
|
63
|
-
server = Rack::Server.new(:app => 'foo')
|
64
|
-
server.middleware['deployment'].flatten.should.include(Rack::TempfileReaper)
|
65
|
-
end
|
66
|
-
|
67
|
-
should "support CGI" do
|
68
|
-
begin
|
69
|
-
o, ENV["REQUEST_METHOD"] = ENV["REQUEST_METHOD"], 'foo'
|
70
|
-
server = Rack::Server.new(:app => 'foo')
|
71
|
-
server.server.name =~ /CGI/
|
72
|
-
Rack::Server.logging_middleware.call(server).should.eql(nil)
|
73
|
-
ensure
|
74
|
-
ENV['REQUEST_METHOD'] = o
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
should "be quiet if said so" do
|
79
|
-
server = Rack::Server.new(:app => "FOO", :quiet => true)
|
80
|
-
Rack::Server.logging_middleware.call(server).should.eql(nil)
|
81
|
-
end
|
82
|
-
|
83
|
-
should "use a full path to the pidfile" do
|
84
|
-
# avoids issues with daemonize chdir
|
85
|
-
opts = Rack::Server.new.send(:parse_options, %w[--pid testing.pid])
|
86
|
-
opts[:pid].should.eql(::File.expand_path('testing.pid'))
|
87
|
-
end
|
88
|
-
|
89
|
-
should "run a server" do
|
90
|
-
pidfile = Tempfile.open('pidfile') { |f| break f }.path
|
91
|
-
FileUtils.rm pidfile
|
92
|
-
server = Rack::Server.new(
|
93
|
-
:app => app,
|
94
|
-
:environment => 'none',
|
95
|
-
:pid => pidfile,
|
96
|
-
:Port => TCPServer.open('127.0.0.1', 0){|s| s.addr[1] },
|
97
|
-
:Host => '127.0.0.1',
|
98
|
-
:daemonize => false,
|
99
|
-
:server => 'webrick'
|
100
|
-
)
|
101
|
-
t = Thread.new { server.start { |s| Thread.current[:server] = s } }
|
102
|
-
t.join(0.01) until t[:server] && t[:server].status != :Stop
|
103
|
-
body = open("http://127.0.0.1:#{server.options[:Port]}/") { |f| f.read }
|
104
|
-
body.should.eql('success')
|
105
|
-
|
106
|
-
Process.kill(:INT, $$)
|
107
|
-
t.join
|
108
|
-
open(pidfile) { |f| f.read.should.eql $$.to_s }
|
109
|
-
end
|
110
|
-
|
111
|
-
should "check pid file presence and running process" do
|
112
|
-
pidfile = Tempfile.open('pidfile') { |f| f.write($$); break f }.path
|
113
|
-
server = Rack::Server.new(:pid => pidfile)
|
114
|
-
server.send(:pidfile_process_status).should.eql :running
|
115
|
-
end
|
116
|
-
|
117
|
-
should "check pid file presence and dead process" do
|
118
|
-
dead_pid = `echo $$`.to_i
|
119
|
-
pidfile = Tempfile.open('pidfile') { |f| f.write(dead_pid); break f }.path
|
120
|
-
server = Rack::Server.new(:pid => pidfile)
|
121
|
-
server.send(:pidfile_process_status).should.eql :dead
|
122
|
-
end
|
123
|
-
|
124
|
-
should "check pid file presence and exited process" do
|
125
|
-
pidfile = Tempfile.open('pidfile') { |f| break f }.path
|
126
|
-
::File.delete(pidfile)
|
127
|
-
server = Rack::Server.new(:pid => pidfile)
|
128
|
-
server.send(:pidfile_process_status).should.eql :exited
|
129
|
-
end
|
130
|
-
|
131
|
-
should "check pid file presence and not owned process" do
|
132
|
-
pidfile = Tempfile.open('pidfile') { |f| f.write(1); break f }.path
|
133
|
-
server = Rack::Server.new(:pid => pidfile)
|
134
|
-
server.send(:pidfile_process_status).should.eql :not_owned
|
135
|
-
end
|
136
|
-
|
137
|
-
should "not write pid file when it is created after check" do
|
138
|
-
pidfile = Tempfile.open('pidfile') { |f| break f }.path
|
139
|
-
::File.delete(pidfile)
|
140
|
-
server = Rack::Server.new(:pid => pidfile)
|
141
|
-
::File.open(pidfile, 'w') { |f| f.write(1) }
|
142
|
-
with_stderr do |err|
|
143
|
-
should.raise(SystemExit) do
|
144
|
-
server.send(:write_pid)
|
145
|
-
end
|
146
|
-
err.rewind
|
147
|
-
output = err.read
|
148
|
-
output.should.match(/already running/)
|
149
|
-
output.should.include? pidfile
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
should "inform the user about existing pidfiles with running processes" do
|
154
|
-
pidfile = Tempfile.open('pidfile') { |f| f.write(1); break f }.path
|
155
|
-
server = Rack::Server.new(:pid => pidfile)
|
156
|
-
with_stderr do |err|
|
157
|
-
should.raise(SystemExit) do
|
158
|
-
server.start
|
159
|
-
end
|
160
|
-
err.rewind
|
161
|
-
output = err.read
|
162
|
-
output.should.match(/already running/)
|
163
|
-
output.should.include? pidfile
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
|
-
end
|
@@ -1,53 +0,0 @@
|
|
1
|
-
### WARNING: there be hax in this file.
|
2
|
-
|
3
|
-
require 'rack/session/abstract/id'
|
4
|
-
|
5
|
-
describe Rack::Session::Abstract::ID do
|
6
|
-
id = Rack::Session::Abstract::ID
|
7
|
-
|
8
|
-
def silence_warning
|
9
|
-
o, $VERBOSE = $VERBOSE, nil
|
10
|
-
yield
|
11
|
-
ensure
|
12
|
-
$VERBOSE = o
|
13
|
-
end
|
14
|
-
|
15
|
-
def reload_id
|
16
|
-
$".delete $".find { |part| part =~ %r{session/abstract/id.rb} }
|
17
|
-
silence_warning { require 'rack/session/abstract/id' }
|
18
|
-
end
|
19
|
-
|
20
|
-
should "use securerandom when available" do
|
21
|
-
begin
|
22
|
-
fake = false
|
23
|
-
silence_warning do
|
24
|
-
::SecureRandom = fake = true unless defined?(SecureRandom)
|
25
|
-
end
|
26
|
-
reload_id
|
27
|
-
id::DEFAULT_OPTIONS[:secure_random].should.eql(fake || SecureRandom)
|
28
|
-
ensure
|
29
|
-
Object.send(:remove_const, :SecureRandom) if fake
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
should "not use securerandom when unavailable" do
|
34
|
-
begin
|
35
|
-
sr = Object.send(:remove_const, :SecureRandom) if defined?(SecureRandom)
|
36
|
-
reload_id
|
37
|
-
id::DEFAULT_OPTIONS[:secure_random].should.eql false
|
38
|
-
ensure
|
39
|
-
::SecureRandom = sr if defined?(sr)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
should "allow to use another securerandom provider" do
|
44
|
-
secure_random = Class.new do
|
45
|
-
def hex(*args)
|
46
|
-
'fake_hex'
|
47
|
-
end
|
48
|
-
end
|
49
|
-
id = Rack::Session::Abstract::ID.new nil, :secure_random => secure_random.new
|
50
|
-
id.send(:generate_sid).should.equal 'fake_hex'
|
51
|
-
end
|
52
|
-
|
53
|
-
end
|
data/test/spec_session_cookie.rb
DELETED
@@ -1,410 +0,0 @@
|
|
1
|
-
require 'rack/session/cookie'
|
2
|
-
require 'rack/lint'
|
3
|
-
require 'rack/mock'
|
4
|
-
|
5
|
-
describe Rack::Session::Cookie do
|
6
|
-
incrementor = lambda do |env|
|
7
|
-
env["rack.session"]["counter"] ||= 0
|
8
|
-
env["rack.session"]["counter"] += 1
|
9
|
-
hash = env["rack.session"].dup
|
10
|
-
hash.delete("session_id")
|
11
|
-
Rack::Response.new(hash.inspect).to_a
|
12
|
-
end
|
13
|
-
|
14
|
-
session_id = lambda do |env|
|
15
|
-
Rack::Response.new(env["rack.session"].to_hash.inspect).to_a
|
16
|
-
end
|
17
|
-
|
18
|
-
session_option = lambda do |opt|
|
19
|
-
lambda do |env|
|
20
|
-
Rack::Response.new(env["rack.session.options"][opt].inspect).to_a
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
nothing = lambda do |env|
|
25
|
-
Rack::Response.new("Nothing").to_a
|
26
|
-
end
|
27
|
-
|
28
|
-
renewer = lambda do |env|
|
29
|
-
env["rack.session.options"][:renew] = true
|
30
|
-
Rack::Response.new("Nothing").to_a
|
31
|
-
end
|
32
|
-
|
33
|
-
only_session_id = lambda do |env|
|
34
|
-
Rack::Response.new(env["rack.session"]["session_id"].to_s).to_a
|
35
|
-
end
|
36
|
-
|
37
|
-
bigcookie = lambda do |env|
|
38
|
-
env["rack.session"]["cookie"] = "big" * 3000
|
39
|
-
Rack::Response.new(env["rack.session"].inspect).to_a
|
40
|
-
end
|
41
|
-
|
42
|
-
destroy_session = lambda do |env|
|
43
|
-
env["rack.session"].destroy
|
44
|
-
Rack::Response.new("Nothing").to_a
|
45
|
-
end
|
46
|
-
|
47
|
-
def response_for(options={})
|
48
|
-
request_options = options.fetch(:request, {})
|
49
|
-
cookie = if options[:cookie].is_a?(Rack::Response)
|
50
|
-
options[:cookie]["Set-Cookie"]
|
51
|
-
else
|
52
|
-
options[:cookie]
|
53
|
-
end
|
54
|
-
request_options["HTTP_COOKIE"] = cookie || ""
|
55
|
-
|
56
|
-
app_with_cookie = Rack::Session::Cookie.new(*options[:app])
|
57
|
-
app_with_cookie = Rack::Lint.new(app_with_cookie)
|
58
|
-
Rack::MockRequest.new(app_with_cookie).get("/", request_options)
|
59
|
-
end
|
60
|
-
|
61
|
-
before do
|
62
|
-
@warnings = warnings = []
|
63
|
-
Rack::Session::Cookie.class_eval do
|
64
|
-
define_method(:warn) { |m| warnings << m }
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
after do
|
69
|
-
Rack::Session::Cookie.class_eval { remove_method :warn }
|
70
|
-
end
|
71
|
-
|
72
|
-
describe 'Base64' do
|
73
|
-
it 'uses base64 to encode' do
|
74
|
-
coder = Rack::Session::Cookie::Base64.new
|
75
|
-
str = 'fuuuuu'
|
76
|
-
coder.encode(str).should.equal [str].pack('m')
|
77
|
-
end
|
78
|
-
|
79
|
-
it 'uses base64 to decode' do
|
80
|
-
coder = Rack::Session::Cookie::Base64.new
|
81
|
-
str = ['fuuuuu'].pack('m')
|
82
|
-
coder.decode(str).should.equal str.unpack('m').first
|
83
|
-
end
|
84
|
-
|
85
|
-
describe 'Marshal' do
|
86
|
-
it 'marshals and base64 encodes' do
|
87
|
-
coder = Rack::Session::Cookie::Base64::Marshal.new
|
88
|
-
str = 'fuuuuu'
|
89
|
-
coder.encode(str).should.equal [::Marshal.dump(str)].pack('m')
|
90
|
-
end
|
91
|
-
|
92
|
-
it 'marshals and base64 decodes' do
|
93
|
-
coder = Rack::Session::Cookie::Base64::Marshal.new
|
94
|
-
str = [::Marshal.dump('fuuuuu')].pack('m')
|
95
|
-
coder.decode(str).should.equal ::Marshal.load(str.unpack('m').first)
|
96
|
-
end
|
97
|
-
|
98
|
-
it 'rescues failures on decode' do
|
99
|
-
coder = Rack::Session::Cookie::Base64::Marshal.new
|
100
|
-
coder.decode('lulz').should.equal nil
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
describe 'JSON' do
|
105
|
-
it 'marshals and base64 encodes' do
|
106
|
-
coder = Rack::Session::Cookie::Base64::JSON.new
|
107
|
-
obj = %w[fuuuuu]
|
108
|
-
coder.encode(obj).should.equal [::Rack::Utils::OkJson.encode(obj)].pack('m')
|
109
|
-
end
|
110
|
-
|
111
|
-
it 'marshals and base64 decodes' do
|
112
|
-
coder = Rack::Session::Cookie::Base64::JSON.new
|
113
|
-
str = [::Rack::Utils::OkJson.encode(%w[fuuuuu])].pack('m')
|
114
|
-
coder.decode(str).should.equal ::Rack::Utils::OkJson.decode(str.unpack('m').first)
|
115
|
-
end
|
116
|
-
|
117
|
-
it 'rescues failures on decode' do
|
118
|
-
coder = Rack::Session::Cookie::Base64::JSON.new
|
119
|
-
coder.decode('lulz').should.equal nil
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
describe 'ZipJSON' do
|
124
|
-
it 'jsons, deflates, and base64 encodes' do
|
125
|
-
coder = Rack::Session::Cookie::Base64::ZipJSON.new
|
126
|
-
obj = %w[fuuuuu]
|
127
|
-
json = Rack::Utils::OkJson.encode(obj)
|
128
|
-
coder.encode(obj).should.equal [Zlib::Deflate.deflate(json)].pack('m')
|
129
|
-
end
|
130
|
-
|
131
|
-
it 'base64 decodes, inflates, and decodes json' do
|
132
|
-
coder = Rack::Session::Cookie::Base64::ZipJSON.new
|
133
|
-
obj = %w[fuuuuu]
|
134
|
-
json = Rack::Utils::OkJson.encode(obj)
|
135
|
-
b64 = [Zlib::Deflate.deflate(json)].pack('m')
|
136
|
-
coder.decode(b64).should.equal obj
|
137
|
-
end
|
138
|
-
|
139
|
-
it 'rescues failures on decode' do
|
140
|
-
coder = Rack::Session::Cookie::Base64::ZipJSON.new
|
141
|
-
coder.decode('lulz').should.equal nil
|
142
|
-
end
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
it "warns if no secret is given" do
|
147
|
-
Rack::Session::Cookie.new(incrementor)
|
148
|
-
@warnings.first.should =~ /no secret/i
|
149
|
-
@warnings.clear
|
150
|
-
Rack::Session::Cookie.new(incrementor, :secret => 'abc')
|
151
|
-
@warnings.should.be.empty?
|
152
|
-
end
|
153
|
-
|
154
|
-
it 'uses a coder' do
|
155
|
-
identity = Class.new {
|
156
|
-
attr_reader :calls
|
157
|
-
|
158
|
-
def initialize
|
159
|
-
@calls = []
|
160
|
-
end
|
161
|
-
|
162
|
-
def encode(str); @calls << :encode; str; end
|
163
|
-
def decode(str); @calls << :decode; str; end
|
164
|
-
}.new
|
165
|
-
response = response_for(:app => [incrementor, { :coder => identity }])
|
166
|
-
|
167
|
-
response["Set-Cookie"].should.include("rack.session=")
|
168
|
-
response.body.should.equal '{"counter"=>1}'
|
169
|
-
identity.calls.should.equal [:decode, :encode]
|
170
|
-
end
|
171
|
-
|
172
|
-
it "creates a new cookie" do
|
173
|
-
response = response_for(:app => incrementor)
|
174
|
-
response["Set-Cookie"].should.include("rack.session=")
|
175
|
-
response.body.should.equal '{"counter"=>1}'
|
176
|
-
end
|
177
|
-
|
178
|
-
it "loads from a cookie" do
|
179
|
-
response = response_for(:app => incrementor)
|
180
|
-
|
181
|
-
response = response_for(:app => incrementor, :cookie => response)
|
182
|
-
response.body.should.equal '{"counter"=>2}'
|
183
|
-
|
184
|
-
response = response_for(:app => incrementor, :cookie => response)
|
185
|
-
response.body.should.equal '{"counter"=>3}'
|
186
|
-
end
|
187
|
-
|
188
|
-
it "renew session id" do
|
189
|
-
response = response_for(:app => incrementor)
|
190
|
-
cookie = response['Set-Cookie']
|
191
|
-
response = response_for(:app => only_session_id, :cookie => cookie)
|
192
|
-
cookie = response['Set-Cookie'] if response['Set-Cookie']
|
193
|
-
|
194
|
-
response.body.should.not.equal ""
|
195
|
-
old_session_id = response.body
|
196
|
-
|
197
|
-
response = response_for(:app => renewer, :cookie => cookie)
|
198
|
-
cookie = response['Set-Cookie'] if response['Set-Cookie']
|
199
|
-
response = response_for(:app => only_session_id, :cookie => cookie)
|
200
|
-
|
201
|
-
response.body.should.not.equal ""
|
202
|
-
response.body.should.not.equal old_session_id
|
203
|
-
end
|
204
|
-
|
205
|
-
it "destroys session" do
|
206
|
-
response = response_for(:app => incrementor)
|
207
|
-
response = response_for(:app => only_session_id, :cookie => response)
|
208
|
-
|
209
|
-
response.body.should.not.equal ""
|
210
|
-
old_session_id = response.body
|
211
|
-
|
212
|
-
response = response_for(:app => destroy_session, :cookie => response)
|
213
|
-
response = response_for(:app => only_session_id, :cookie => response)
|
214
|
-
|
215
|
-
response.body.should.not.equal ""
|
216
|
-
response.body.should.not.equal old_session_id
|
217
|
-
end
|
218
|
-
|
219
|
-
it "survives broken cookies" do
|
220
|
-
response = response_for(
|
221
|
-
:app => incrementor,
|
222
|
-
:cookie => "rack.session=blarghfasel"
|
223
|
-
)
|
224
|
-
response.body.should.equal '{"counter"=>1}'
|
225
|
-
|
226
|
-
response = response_for(
|
227
|
-
:app => [incrementor, { :secret => "test" }],
|
228
|
-
:cookie => "rack.session="
|
229
|
-
)
|
230
|
-
response.body.should.equal '{"counter"=>1}'
|
231
|
-
end
|
232
|
-
|
233
|
-
it "barks on too big cookies" do
|
234
|
-
lambda{
|
235
|
-
response_for(:app => bigcookie, :request => { :fatal => true })
|
236
|
-
}.should.raise(Rack::MockRequest::FatalWarning)
|
237
|
-
end
|
238
|
-
|
239
|
-
it "loads from a cookie with integrity hash" do
|
240
|
-
app = [incrementor, { :secret => "test" }]
|
241
|
-
|
242
|
-
response = response_for(:app => app)
|
243
|
-
response = response_for(:app => app, :cookie => response)
|
244
|
-
response.body.should.equal '{"counter"=>2}'
|
245
|
-
|
246
|
-
response = response_for(:app => app, :cookie => response)
|
247
|
-
response.body.should.equal '{"counter"=>3}'
|
248
|
-
|
249
|
-
app = [incrementor, { :secret => "other" }]
|
250
|
-
|
251
|
-
response = response_for(:app => app, :cookie => response)
|
252
|
-
response.body.should.equal '{"counter"=>1}'
|
253
|
-
end
|
254
|
-
|
255
|
-
it "loads from a cookie wih accept-only integrity hash for graceful key rotation" do
|
256
|
-
response = response_for(:app => [incrementor, { :secret => "test" }])
|
257
|
-
|
258
|
-
app = [incrementor, { :secret => "test2", :old_secret => "test" }]
|
259
|
-
response = response_for(:app => app, :cookie => response)
|
260
|
-
response.body.should.equal '{"counter"=>2}'
|
261
|
-
|
262
|
-
app = [incrementor, { :secret => "test3", :old_secret => "test2" }]
|
263
|
-
response = response_for(:app => app, :cookie => response)
|
264
|
-
response.body.should.equal '{"counter"=>3}'
|
265
|
-
end
|
266
|
-
|
267
|
-
it "ignores tampered with session cookies" do
|
268
|
-
app = [incrementor, { :secret => "test" }]
|
269
|
-
response = response_for(:app => app)
|
270
|
-
response.body.should.equal '{"counter"=>1}'
|
271
|
-
|
272
|
-
response = response_for(:app => app, :cookie => response)
|
273
|
-
response.body.should.equal '{"counter"=>2}'
|
274
|
-
|
275
|
-
_, digest = response["Set-Cookie"].split("--")
|
276
|
-
tampered_with_cookie = "hackerman-was-here" + "--" + digest
|
277
|
-
|
278
|
-
response = response_for(:app => app, :cookie => tampered_with_cookie)
|
279
|
-
response.body.should.equal '{"counter"=>1}'
|
280
|
-
end
|
281
|
-
|
282
|
-
it "supports either of secret or old_secret" do
|
283
|
-
app = [incrementor, { :secret => "test" }]
|
284
|
-
response = response_for(:app => app)
|
285
|
-
response.body.should.equal '{"counter"=>1}'
|
286
|
-
|
287
|
-
response = response_for(:app => app, :cookie => response)
|
288
|
-
response.body.should.equal '{"counter"=>2}'
|
289
|
-
|
290
|
-
app = [incrementor, { :old_secret => "test" }]
|
291
|
-
response = response_for(:app => app)
|
292
|
-
response.body.should.equal '{"counter"=>1}'
|
293
|
-
|
294
|
-
response = response_for(:app => app, :cookie => response)
|
295
|
-
response.body.should.equal '{"counter"=>2}'
|
296
|
-
end
|
297
|
-
|
298
|
-
it "can handle Rack::Lint middleware" do
|
299
|
-
response = response_for(:app => incrementor)
|
300
|
-
|
301
|
-
lint = Rack::Lint.new(session_id)
|
302
|
-
response = response_for(:app => lint, :cookie => response)
|
303
|
-
response.body.should.not.be.nil
|
304
|
-
end
|
305
|
-
|
306
|
-
it "can handle middleware that inspects the env" do
|
307
|
-
class TestEnvInspector
|
308
|
-
def initialize(app)
|
309
|
-
@app = app
|
310
|
-
end
|
311
|
-
def call(env)
|
312
|
-
env.inspect
|
313
|
-
@app.call(env)
|
314
|
-
end
|
315
|
-
end
|
316
|
-
|
317
|
-
response = response_for(:app => incrementor)
|
318
|
-
|
319
|
-
inspector = TestEnvInspector.new(session_id)
|
320
|
-
response = response_for(:app => inspector, :cookie => response)
|
321
|
-
response.body.should.not.be.nil
|
322
|
-
end
|
323
|
-
|
324
|
-
it "returns the session id in the session hash" do
|
325
|
-
response = response_for(:app => incrementor)
|
326
|
-
response.body.should.equal '{"counter"=>1}'
|
327
|
-
|
328
|
-
response = response_for(:app => session_id, :cookie => response)
|
329
|
-
response.body.should.match(/"session_id"=>/)
|
330
|
-
response.body.should.match(/"counter"=>1/)
|
331
|
-
end
|
332
|
-
|
333
|
-
it "does not return a cookie if set to secure but not using ssl" do
|
334
|
-
app = [incrementor, { :secure => true }]
|
335
|
-
|
336
|
-
response = response_for(:app => app)
|
337
|
-
response["Set-Cookie"].should.be.nil
|
338
|
-
|
339
|
-
response = response_for(:app => app, :request => { "HTTPS" => "on" })
|
340
|
-
response["Set-Cookie"].should.not.be.nil
|
341
|
-
response["Set-Cookie"].should.match(/secure/)
|
342
|
-
end
|
343
|
-
|
344
|
-
it "does not return a cookie if cookie was not read/written" do
|
345
|
-
response = response_for(:app => nothing)
|
346
|
-
response["Set-Cookie"].should.be.nil
|
347
|
-
end
|
348
|
-
|
349
|
-
it "does not return a cookie if cookie was not written (only read)" do
|
350
|
-
response = response_for(:app => session_id)
|
351
|
-
response["Set-Cookie"].should.be.nil
|
352
|
-
end
|
353
|
-
|
354
|
-
it "returns even if not read/written if :expire_after is set" do
|
355
|
-
app = [nothing, { :expire_after => 3600 }]
|
356
|
-
request = { "rack.session" => { "not" => "empty" }}
|
357
|
-
response = response_for(:app => app, :request => request)
|
358
|
-
response["Set-Cookie"].should.not.be.nil
|
359
|
-
end
|
360
|
-
|
361
|
-
it "returns no cookie if no data was written and no session was created previously, even if :expire_after is set" do
|
362
|
-
app = [nothing, { :expire_after => 3600 }]
|
363
|
-
response = response_for(:app => app)
|
364
|
-
response["Set-Cookie"].should.be.nil
|
365
|
-
end
|
366
|
-
|
367
|
-
it "exposes :secret in env['rack.session.option']" do
|
368
|
-
response = response_for(:app => [session_option[:secret], { :secret => "foo" }])
|
369
|
-
response.body.should == '"foo"'
|
370
|
-
end
|
371
|
-
|
372
|
-
it "exposes :coder in env['rack.session.option']" do
|
373
|
-
response = response_for(:app => session_option[:coder])
|
374
|
-
response.body.should.match(/Base64::Marshal/)
|
375
|
-
end
|
376
|
-
|
377
|
-
it "allows passing in a hash with session data from middleware in front" do
|
378
|
-
request = { 'rack.session' => { :foo => 'bar' }}
|
379
|
-
response = response_for(:app => session_id, :request => request)
|
380
|
-
response.body.should.match(/foo/)
|
381
|
-
end
|
382
|
-
|
383
|
-
it "allows modifying session data with session data from middleware in front" do
|
384
|
-
request = { 'rack.session' => { :foo => 'bar' }}
|
385
|
-
response = response_for(:app => incrementor, :request => request)
|
386
|
-
response.body.should.match(/counter/)
|
387
|
-
response.body.should.match(/foo/)
|
388
|
-
end
|
389
|
-
|
390
|
-
it "allows more than one '--' in the cookie when calculating digests" do
|
391
|
-
@counter = 0
|
392
|
-
app = lambda do |env|
|
393
|
-
env["rack.session"]["message"] ||= ""
|
394
|
-
env["rack.session"]["message"] << "#{(@counter += 1).to_s}--"
|
395
|
-
hash = env["rack.session"].dup
|
396
|
-
hash.delete("session_id")
|
397
|
-
Rack::Response.new(hash["message"]).to_a
|
398
|
-
end
|
399
|
-
# another example of an unsafe coder is Base64.urlsafe_encode64
|
400
|
-
unsafe_coder = Class.new {
|
401
|
-
def encode(hash); hash.inspect end
|
402
|
-
def decode(str); eval(str) if str; end
|
403
|
-
}.new
|
404
|
-
_app = [ app, { :secret => "test", :coder => unsafe_coder } ]
|
405
|
-
response = response_for(:app => _app)
|
406
|
-
response.body.should.equal "1--"
|
407
|
-
response = response_for(:app => _app, :cookie => response)
|
408
|
-
response.body.should.equal "1--2--"
|
409
|
-
end
|
410
|
-
end
|