rack 1.6.11 → 2.1.4
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 +77 -0
- data/{COPYING → MIT-LICENSE} +4 -2
- data/README.rdoc +89 -139
- 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} +11 -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 +54 -51
- 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 +280 -161
- data/lib/rack/multipart/uploaded_file.rb +3 -2
- data/lib/rack/multipart.rb +39 -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 +215 -94
- data/lib/rack/session/cookie.rb +45 -28
- data/lib/rack/session/memcache.rb +4 -87
- data/lib/rack/session/pool.rb +25 -16
- 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 +186 -272
- data/lib/rack.rb +76 -24
- data/rack.gemspec +25 -14
- metadata +62 -182
- 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 -321
- data/test/spec_session_pool.rb +0 -209
- 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_body_proxy.rb
DELETED
@@ -1,85 +0,0 @@
|
|
1
|
-
require 'rack/body_proxy'
|
2
|
-
require 'stringio'
|
3
|
-
require 'ostruct'
|
4
|
-
|
5
|
-
describe Rack::BodyProxy do
|
6
|
-
should 'call each on the wrapped body' do
|
7
|
-
called = false
|
8
|
-
proxy = Rack::BodyProxy.new(['foo']) { }
|
9
|
-
proxy.each do |str|
|
10
|
-
called = true
|
11
|
-
str.should.equal 'foo'
|
12
|
-
end
|
13
|
-
called.should.equal true
|
14
|
-
end
|
15
|
-
|
16
|
-
should 'call close on the wrapped body' do
|
17
|
-
body = StringIO.new
|
18
|
-
proxy = Rack::BodyProxy.new(body) { }
|
19
|
-
proxy.close
|
20
|
-
body.should.be.closed
|
21
|
-
end
|
22
|
-
|
23
|
-
should 'only call close on the wrapped body if it responds to close' do
|
24
|
-
body = []
|
25
|
-
proxy = Rack::BodyProxy.new(body) { }
|
26
|
-
proc { proxy.close }.should.not.raise
|
27
|
-
end
|
28
|
-
|
29
|
-
should 'call the passed block on close' do
|
30
|
-
called = false
|
31
|
-
proxy = Rack::BodyProxy.new([]) { called = true }
|
32
|
-
called.should.equal false
|
33
|
-
proxy.close
|
34
|
-
called.should.equal true
|
35
|
-
end
|
36
|
-
|
37
|
-
should 'call the passed block on close even if there is an exception' do
|
38
|
-
object = Object.new
|
39
|
-
def object.close() raise "No!" end
|
40
|
-
called = false
|
41
|
-
|
42
|
-
begin
|
43
|
-
proxy = Rack::BodyProxy.new(object) { called = true }
|
44
|
-
called.should.equal false
|
45
|
-
proxy.close
|
46
|
-
rescue RuntimeError => e
|
47
|
-
end
|
48
|
-
|
49
|
-
raise "Expected exception to have been raised" unless e
|
50
|
-
called.should.equal true
|
51
|
-
end
|
52
|
-
|
53
|
-
should 'allow multiple arguments in respond_to?' do
|
54
|
-
body = []
|
55
|
-
proxy = Rack::BodyProxy.new(body) { }
|
56
|
-
proc { proxy.respond_to?(:foo, false) }.should.not.raise
|
57
|
-
end
|
58
|
-
|
59
|
-
should 'not respond to :to_ary' do
|
60
|
-
body = OpenStruct.new(:to_ary => true)
|
61
|
-
body.respond_to?(:to_ary).should.equal true
|
62
|
-
|
63
|
-
proxy = Rack::BodyProxy.new(body) { }
|
64
|
-
proxy.respond_to?(:to_ary).should.equal false
|
65
|
-
proxy.respond_to?("to_ary").should.equal false
|
66
|
-
end
|
67
|
-
|
68
|
-
should 'not close more than one time' do
|
69
|
-
count = 0
|
70
|
-
proxy = Rack::BodyProxy.new([]) { count += 1; raise "Block invoked more than 1 time!" if count > 1 }
|
71
|
-
2.times { proxy.close }
|
72
|
-
count.should.equal 1
|
73
|
-
end
|
74
|
-
|
75
|
-
should 'be closed when the callback is triggered' do
|
76
|
-
closed = false
|
77
|
-
proxy = Rack::BodyProxy.new([]) { closed = proxy.closed? }
|
78
|
-
proxy.close
|
79
|
-
closed.should.equal true
|
80
|
-
end
|
81
|
-
|
82
|
-
should 'provide an #each method' do
|
83
|
-
Rack::BodyProxy.method_defined?(:each).should.equal true
|
84
|
-
end
|
85
|
-
end
|
data/test/spec_builder.rb
DELETED
@@ -1,223 +0,0 @@
|
|
1
|
-
require 'rack/builder'
|
2
|
-
require 'rack/lint'
|
3
|
-
require 'rack/mock'
|
4
|
-
require 'rack/showexceptions'
|
5
|
-
require 'rack/urlmap'
|
6
|
-
|
7
|
-
class NothingMiddleware
|
8
|
-
def initialize(app)
|
9
|
-
@app = app
|
10
|
-
end
|
11
|
-
def call(env)
|
12
|
-
@@env = env
|
13
|
-
response = @app.call(env)
|
14
|
-
response
|
15
|
-
end
|
16
|
-
def self.env
|
17
|
-
@@env
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
describe Rack::Builder do
|
22
|
-
def builder(&block)
|
23
|
-
Rack::Lint.new Rack::Builder.new(&block)
|
24
|
-
end
|
25
|
-
|
26
|
-
def builder_to_app(&block)
|
27
|
-
Rack::Lint.new Rack::Builder.new(&block).to_app
|
28
|
-
end
|
29
|
-
|
30
|
-
it "supports mapping" do
|
31
|
-
app = builder_to_app do
|
32
|
-
map '/' do |outer_env|
|
33
|
-
run lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, ['root']] }
|
34
|
-
end
|
35
|
-
map '/sub' do
|
36
|
-
run lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, ['sub']] }
|
37
|
-
end
|
38
|
-
end
|
39
|
-
Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'root'
|
40
|
-
Rack::MockRequest.new(app).get("/sub").body.to_s.should.equal 'sub'
|
41
|
-
end
|
42
|
-
|
43
|
-
it "doesn't dupe env even when mapping" do
|
44
|
-
app = builder_to_app do
|
45
|
-
use NothingMiddleware
|
46
|
-
map '/' do |outer_env|
|
47
|
-
run lambda { |inner_env|
|
48
|
-
inner_env['new_key'] = 'new_value'
|
49
|
-
[200, {"Content-Type" => "text/plain"}, ['root']]
|
50
|
-
}
|
51
|
-
end
|
52
|
-
end
|
53
|
-
Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'root'
|
54
|
-
NothingMiddleware.env['new_key'].should.equal 'new_value'
|
55
|
-
end
|
56
|
-
|
57
|
-
it "chains apps by default" do
|
58
|
-
app = builder_to_app do
|
59
|
-
use Rack::ShowExceptions
|
60
|
-
run lambda { |env| raise "bzzzt" }
|
61
|
-
end
|
62
|
-
|
63
|
-
Rack::MockRequest.new(app).get("/").should.be.server_error
|
64
|
-
Rack::MockRequest.new(app).get("/").should.be.server_error
|
65
|
-
Rack::MockRequest.new(app).get("/").should.be.server_error
|
66
|
-
end
|
67
|
-
|
68
|
-
it "has implicit #to_app" do
|
69
|
-
app = builder do
|
70
|
-
use Rack::ShowExceptions
|
71
|
-
run lambda { |env| raise "bzzzt" }
|
72
|
-
end
|
73
|
-
|
74
|
-
Rack::MockRequest.new(app).get("/").should.be.server_error
|
75
|
-
Rack::MockRequest.new(app).get("/").should.be.server_error
|
76
|
-
Rack::MockRequest.new(app).get("/").should.be.server_error
|
77
|
-
end
|
78
|
-
|
79
|
-
it "supports blocks on use" do
|
80
|
-
app = builder do
|
81
|
-
use Rack::ShowExceptions
|
82
|
-
use Rack::Auth::Basic do |username, password|
|
83
|
-
'secret' == password
|
84
|
-
end
|
85
|
-
|
86
|
-
run lambda { |env| [200, {"Content-Type" => "text/plain"}, ['Hi Boss']] }
|
87
|
-
end
|
88
|
-
|
89
|
-
response = Rack::MockRequest.new(app).get("/")
|
90
|
-
response.should.be.client_error
|
91
|
-
response.status.should.equal 401
|
92
|
-
|
93
|
-
# with auth...
|
94
|
-
response = Rack::MockRequest.new(app).get("/",
|
95
|
-
'HTTP_AUTHORIZATION' => 'Basic ' + ["joe:secret"].pack("m*"))
|
96
|
-
response.status.should.equal 200
|
97
|
-
response.body.to_s.should.equal 'Hi Boss'
|
98
|
-
end
|
99
|
-
|
100
|
-
it "has explicit #to_app" do
|
101
|
-
app = builder do
|
102
|
-
use Rack::ShowExceptions
|
103
|
-
run lambda { |env| raise "bzzzt" }
|
104
|
-
end
|
105
|
-
|
106
|
-
Rack::MockRequest.new(app).get("/").should.be.server_error
|
107
|
-
Rack::MockRequest.new(app).get("/").should.be.server_error
|
108
|
-
Rack::MockRequest.new(app).get("/").should.be.server_error
|
109
|
-
end
|
110
|
-
|
111
|
-
it "can mix map and run for endpoints" do
|
112
|
-
app = builder do
|
113
|
-
map '/sub' do
|
114
|
-
run lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, ['sub']] }
|
115
|
-
end
|
116
|
-
run lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, ['root']] }
|
117
|
-
end
|
118
|
-
|
119
|
-
Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'root'
|
120
|
-
Rack::MockRequest.new(app).get("/sub").body.to_s.should.equal 'sub'
|
121
|
-
end
|
122
|
-
|
123
|
-
it "accepts middleware-only map blocks" do
|
124
|
-
app = builder do
|
125
|
-
map('/foo') { use Rack::ShowExceptions }
|
126
|
-
run lambda { |env| raise "bzzzt" }
|
127
|
-
end
|
128
|
-
|
129
|
-
proc { Rack::MockRequest.new(app).get("/") }.should.raise(RuntimeError)
|
130
|
-
Rack::MockRequest.new(app).get("/foo").should.be.server_error
|
131
|
-
end
|
132
|
-
|
133
|
-
it "yields the generated app to a block for warmup" do
|
134
|
-
warmed_up_app = nil
|
135
|
-
|
136
|
-
app = Rack::Builder.new do
|
137
|
-
warmup { |a| warmed_up_app = a }
|
138
|
-
run lambda { |env| [200, {}, []] }
|
139
|
-
end.to_app
|
140
|
-
|
141
|
-
warmed_up_app.should.equal app
|
142
|
-
end
|
143
|
-
|
144
|
-
should "initialize apps once" do
|
145
|
-
app = builder do
|
146
|
-
class AppClass
|
147
|
-
def initialize
|
148
|
-
@called = 0
|
149
|
-
end
|
150
|
-
def call(env)
|
151
|
-
raise "bzzzt" if @called > 0
|
152
|
-
@called += 1
|
153
|
-
[200, {'Content-Type' => 'text/plain'}, ['OK']]
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
use Rack::ShowExceptions
|
158
|
-
run AppClass.new
|
159
|
-
end
|
160
|
-
|
161
|
-
Rack::MockRequest.new(app).get("/").status.should.equal 200
|
162
|
-
Rack::MockRequest.new(app).get("/").should.be.server_error
|
163
|
-
end
|
164
|
-
|
165
|
-
it "allows use after run" do
|
166
|
-
app = builder do
|
167
|
-
run lambda { |env| raise "bzzzt" }
|
168
|
-
use Rack::ShowExceptions
|
169
|
-
end
|
170
|
-
|
171
|
-
Rack::MockRequest.new(app).get("/").should.be.server_error
|
172
|
-
Rack::MockRequest.new(app).get("/").should.be.server_error
|
173
|
-
Rack::MockRequest.new(app).get("/").should.be.server_error
|
174
|
-
end
|
175
|
-
|
176
|
-
it 'complains about a missing run' do
|
177
|
-
proc do
|
178
|
-
Rack::Lint.new Rack::Builder.app { use Rack::ShowExceptions }
|
179
|
-
end.should.raise(RuntimeError)
|
180
|
-
end
|
181
|
-
|
182
|
-
describe "parse_file" do
|
183
|
-
def config_file(name)
|
184
|
-
File.join(File.dirname(__FILE__), 'builder', name)
|
185
|
-
end
|
186
|
-
|
187
|
-
it "parses commented options" do
|
188
|
-
app, options = Rack::Builder.parse_file config_file('options.ru')
|
189
|
-
options[:debug].should.be.true
|
190
|
-
Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'OK'
|
191
|
-
end
|
192
|
-
|
193
|
-
it "removes __END__ before evaluating app" do
|
194
|
-
app, _ = Rack::Builder.parse_file config_file('end.ru')
|
195
|
-
Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'OK'
|
196
|
-
end
|
197
|
-
|
198
|
-
it "supports multi-line comments" do
|
199
|
-
lambda {
|
200
|
-
Rack::Builder.parse_file config_file('comment.ru')
|
201
|
-
}.should.not.raise(SyntaxError)
|
202
|
-
end
|
203
|
-
|
204
|
-
it "requires anything not ending in .ru" do
|
205
|
-
$: << File.dirname(__FILE__)
|
206
|
-
app, * = Rack::Builder.parse_file 'builder/anything'
|
207
|
-
Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'OK'
|
208
|
-
$:.pop
|
209
|
-
end
|
210
|
-
|
211
|
-
it "sets __LINE__ correctly" do
|
212
|
-
app, _ = Rack::Builder.parse_file config_file('line.ru')
|
213
|
-
Rack::MockRequest.new(app).get("/").body.to_s.should.equal '1'
|
214
|
-
end
|
215
|
-
end
|
216
|
-
|
217
|
-
describe 'new_from_string' do
|
218
|
-
it "builds a rack app from string" do
|
219
|
-
app, = Rack::Builder.new_from_string "run lambda{|env| [200, {'Content-Type' => 'text/plane'}, ['OK']] }"
|
220
|
-
Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'OK'
|
221
|
-
end
|
222
|
-
end
|
223
|
-
end
|
data/test/spec_cascade.rb
DELETED
@@ -1,61 +0,0 @@
|
|
1
|
-
require 'rack/cascade'
|
2
|
-
require 'rack/file'
|
3
|
-
require 'rack/lint'
|
4
|
-
require 'rack/urlmap'
|
5
|
-
require 'rack/mock'
|
6
|
-
|
7
|
-
describe Rack::Cascade do
|
8
|
-
def cascade(*args)
|
9
|
-
Rack::Lint.new Rack::Cascade.new(*args)
|
10
|
-
end
|
11
|
-
|
12
|
-
docroot = File.expand_path(File.dirname(__FILE__))
|
13
|
-
app1 = Rack::File.new(docroot)
|
14
|
-
|
15
|
-
app2 = Rack::URLMap.new("/crash" => lambda { |env| raise "boom" })
|
16
|
-
|
17
|
-
app3 = Rack::URLMap.new("/foo" => lambda { |env|
|
18
|
-
[200, { "Content-Type" => "text/plain"}, [""]]})
|
19
|
-
|
20
|
-
should "dispatch onward on 404 and 405 by default" do
|
21
|
-
cascade = cascade([app1, app2, app3])
|
22
|
-
Rack::MockRequest.new(cascade).get("/cgi/test").should.be.ok
|
23
|
-
Rack::MockRequest.new(cascade).get("/foo").should.be.ok
|
24
|
-
Rack::MockRequest.new(cascade).get("/toobad").should.be.not_found
|
25
|
-
Rack::MockRequest.new(cascade).get("/cgi/../..").should.be.client_error
|
26
|
-
|
27
|
-
# Put is not allowed by Rack::File so it'll 405.
|
28
|
-
Rack::MockRequest.new(cascade).put("/foo").should.be.ok
|
29
|
-
end
|
30
|
-
|
31
|
-
should "dispatch onward on whatever is passed" do
|
32
|
-
cascade = cascade([app1, app2, app3], [404, 403])
|
33
|
-
Rack::MockRequest.new(cascade).get("/cgi/../bla").should.be.not_found
|
34
|
-
end
|
35
|
-
|
36
|
-
should "return 404 if empty" do
|
37
|
-
Rack::MockRequest.new(cascade([])).get('/').should.be.not_found
|
38
|
-
end
|
39
|
-
|
40
|
-
should "append new app" do
|
41
|
-
cascade = Rack::Cascade.new([], [404, 403])
|
42
|
-
Rack::MockRequest.new(cascade).get('/').should.be.not_found
|
43
|
-
cascade << app2
|
44
|
-
Rack::MockRequest.new(cascade).get('/cgi/test').should.be.not_found
|
45
|
-
Rack::MockRequest.new(cascade).get('/cgi/../bla').should.be.not_found
|
46
|
-
cascade << app1
|
47
|
-
Rack::MockRequest.new(cascade).get('/cgi/test').should.be.ok
|
48
|
-
Rack::MockRequest.new(cascade).get('/cgi/../..').should.be.client_error
|
49
|
-
Rack::MockRequest.new(cascade).get('/foo').should.be.not_found
|
50
|
-
cascade << app3
|
51
|
-
Rack::MockRequest.new(cascade).get('/foo').should.be.ok
|
52
|
-
end
|
53
|
-
|
54
|
-
should "close the body on cascade" do
|
55
|
-
body = StringIO.new
|
56
|
-
closer = lambda { |env| [404, {}, body] }
|
57
|
-
cascade = Rack::Cascade.new([closer, app3], [404])
|
58
|
-
Rack::MockRequest.new(cascade).get("/foo").should.be.ok
|
59
|
-
body.should.be.closed
|
60
|
-
end
|
61
|
-
end
|
data/test/spec_cgi.rb
DELETED
@@ -1,102 +0,0 @@
|
|
1
|
-
begin
|
2
|
-
require File.expand_path('../testrequest', __FILE__)
|
3
|
-
require 'rack/handler/cgi'
|
4
|
-
|
5
|
-
describe Rack::Handler::CGI do
|
6
|
-
extend TestRequest::Helpers
|
7
|
-
|
8
|
-
@host = '127.0.0.1'
|
9
|
-
@port = 9203
|
10
|
-
|
11
|
-
if `which lighttpd` && !$?.success?
|
12
|
-
raise "lighttpd not found"
|
13
|
-
end
|
14
|
-
|
15
|
-
# Keep this first.
|
16
|
-
$pid = fork {
|
17
|
-
ENV['RACK_ENV'] = 'deployment'
|
18
|
-
ENV['RUBYLIB'] = [
|
19
|
-
File.expand_path('../../lib', __FILE__),
|
20
|
-
ENV['RUBYLIB'],
|
21
|
-
].compact.join(':')
|
22
|
-
|
23
|
-
Dir.chdir(File.expand_path("../cgi", __FILE__)) do
|
24
|
-
exec "lighttpd -D -f lighttpd.conf"
|
25
|
-
end
|
26
|
-
}
|
27
|
-
|
28
|
-
should "respond" do
|
29
|
-
sleep 1
|
30
|
-
GET("/test")
|
31
|
-
response.should.not.be.nil
|
32
|
-
end
|
33
|
-
|
34
|
-
should "be a lighttpd" do
|
35
|
-
GET("/test")
|
36
|
-
status.should.equal 200
|
37
|
-
response["SERVER_SOFTWARE"].should =~ /lighttpd/
|
38
|
-
response["HTTP_VERSION"].should.equal "HTTP/1.1"
|
39
|
-
response["SERVER_PROTOCOL"].should.equal "HTTP/1.1"
|
40
|
-
response["SERVER_PORT"].should.equal @port.to_s
|
41
|
-
response["SERVER_NAME"].should.equal @host
|
42
|
-
end
|
43
|
-
|
44
|
-
should "have rack headers" do
|
45
|
-
GET("/test")
|
46
|
-
response["rack.version"].should.equal([1,3])
|
47
|
-
response["rack.multithread"].should.be.false
|
48
|
-
response["rack.multiprocess"].should.be.true
|
49
|
-
response["rack.run_once"].should.be.true
|
50
|
-
end
|
51
|
-
|
52
|
-
should "have CGI headers on GET" do
|
53
|
-
GET("/test")
|
54
|
-
response["REQUEST_METHOD"].should.equal "GET"
|
55
|
-
response["SCRIPT_NAME"].should.equal "/test"
|
56
|
-
response["REQUEST_PATH"].should.equal "/"
|
57
|
-
response["PATH_INFO"].should.be.nil
|
58
|
-
response["QUERY_STRING"].should.equal ""
|
59
|
-
response["test.postdata"].should.equal ""
|
60
|
-
|
61
|
-
GET("/test/foo?quux=1")
|
62
|
-
response["REQUEST_METHOD"].should.equal "GET"
|
63
|
-
response["SCRIPT_NAME"].should.equal "/test"
|
64
|
-
response["REQUEST_PATH"].should.equal "/"
|
65
|
-
response["PATH_INFO"].should.equal "/foo"
|
66
|
-
response["QUERY_STRING"].should.equal "quux=1"
|
67
|
-
end
|
68
|
-
|
69
|
-
should "have CGI headers on POST" do
|
70
|
-
POST("/test", {"rack-form-data" => "23"}, {'X-test-header' => '42'})
|
71
|
-
status.should.equal 200
|
72
|
-
response["REQUEST_METHOD"].should.equal "POST"
|
73
|
-
response["SCRIPT_NAME"].should.equal "/test"
|
74
|
-
response["REQUEST_PATH"].should.equal "/"
|
75
|
-
response["QUERY_STRING"].should.equal ""
|
76
|
-
response["HTTP_X_TEST_HEADER"].should.equal "42"
|
77
|
-
response["test.postdata"].should.equal "rack-form-data=23"
|
78
|
-
end
|
79
|
-
|
80
|
-
should "support HTTP auth" do
|
81
|
-
GET("/test", {:user => "ruth", :passwd => "secret"})
|
82
|
-
response["HTTP_AUTHORIZATION"].should.equal "Basic cnV0aDpzZWNyZXQ="
|
83
|
-
end
|
84
|
-
|
85
|
-
should "set status" do
|
86
|
-
GET("/test?secret")
|
87
|
-
status.should.equal 403
|
88
|
-
response["rack.url_scheme"].should.equal "http"
|
89
|
-
end
|
90
|
-
|
91
|
-
# Keep this last.
|
92
|
-
should "shutdown" do
|
93
|
-
Process.kill 15, $pid
|
94
|
-
Process.wait($pid).should == $pid
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
rescue RuntimeError
|
99
|
-
$stderr.puts "Skipping Rack::Handler::CGI tests (lighttpd is required). Install lighttpd and try again."
|
100
|
-
rescue NotImplementedError
|
101
|
-
$stderr.puts "Your Ruby implemenation or platform does not support fork. Skipping Rack::Handler::CGI tests."
|
102
|
-
end
|
data/test/spec_chunked.rb
DELETED
@@ -1,101 +0,0 @@
|
|
1
|
-
require 'rack/chunked'
|
2
|
-
require 'rack/lint'
|
3
|
-
require 'rack/mock'
|
4
|
-
|
5
|
-
describe Rack::Chunked do
|
6
|
-
def chunked(app)
|
7
|
-
proc do |env|
|
8
|
-
app = Rack::Chunked.new(app)
|
9
|
-
response = Rack::Lint.new(app).call(env)
|
10
|
-
# we want to use body like an array, but it only has #each
|
11
|
-
response[2] = response[2].to_enum.to_a
|
12
|
-
response
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
before do
|
17
|
-
@env = Rack::MockRequest.
|
18
|
-
env_for('/', 'HTTP_VERSION' => '1.1', 'REQUEST_METHOD' => 'GET')
|
19
|
-
end
|
20
|
-
|
21
|
-
should 'chunk responses with no Content-Length' do
|
22
|
-
app = lambda { |env| [200, {"Content-Type" => "text/plain"}, ['Hello', ' ', 'World!']] }
|
23
|
-
response = Rack::MockResponse.new(*chunked(app).call(@env))
|
24
|
-
response.headers.should.not.include 'Content-Length'
|
25
|
-
response.headers['Transfer-Encoding'].should.equal 'chunked'
|
26
|
-
response.body.should.equal "5\r\nHello\r\n1\r\n \r\n6\r\nWorld!\r\n0\r\n\r\n"
|
27
|
-
end
|
28
|
-
|
29
|
-
should 'chunks empty bodies properly' do
|
30
|
-
app = lambda { |env| [200, {"Content-Type" => "text/plain"}, []] }
|
31
|
-
response = Rack::MockResponse.new(*chunked(app).call(@env))
|
32
|
-
response.headers.should.not.include 'Content-Length'
|
33
|
-
response.headers['Transfer-Encoding'].should.equal 'chunked'
|
34
|
-
response.body.should.equal "0\r\n\r\n"
|
35
|
-
end
|
36
|
-
|
37
|
-
should 'chunks encoded bodies properly' do
|
38
|
-
body = ["\uFFFEHello", " ", "World"].map {|t| t.encode("UTF-16LE") }
|
39
|
-
app = lambda { |env| [200, {"Content-Type" => "text/plain"}, body] }
|
40
|
-
response = Rack::MockResponse.new(*chunked(app).call(@env))
|
41
|
-
response.headers.should.not.include 'Content-Length'
|
42
|
-
response.headers['Transfer-Encoding'].should.equal 'chunked'
|
43
|
-
response.body.encoding.to_s.should.equal "ASCII-8BIT"
|
44
|
-
response.body.should.equal "c\r\n\xFE\xFFH\x00e\x00l\x00l\x00o\x00\r\n2\r\n \x00\r\na\r\nW\x00o\x00r\x00l\x00d\x00\r\n0\r\n\r\n".force_encoding("BINARY")
|
45
|
-
end if RUBY_VERSION >= "1.9"
|
46
|
-
|
47
|
-
should 'not modify response when Content-Length header present' do
|
48
|
-
app = lambda { |env|
|
49
|
-
[200, {"Content-Type" => "text/plain", 'Content-Length'=>'12'}, ['Hello', ' ', 'World!']]
|
50
|
-
}
|
51
|
-
status, headers, body = chunked(app).call(@env)
|
52
|
-
status.should.equal 200
|
53
|
-
headers.should.not.include 'Transfer-Encoding'
|
54
|
-
headers.should.include 'Content-Length'
|
55
|
-
body.join.should.equal 'Hello World!'
|
56
|
-
end
|
57
|
-
|
58
|
-
should 'not modify response when client is HTTP/1.0' do
|
59
|
-
app = lambda { |env| [200, {"Content-Type" => "text/plain"}, ['Hello', ' ', 'World!']] }
|
60
|
-
@env['HTTP_VERSION'] = 'HTTP/1.0'
|
61
|
-
status, headers, body = chunked(app).call(@env)
|
62
|
-
status.should.equal 200
|
63
|
-
headers.should.not.include 'Transfer-Encoding'
|
64
|
-
body.join.should.equal 'Hello World!'
|
65
|
-
end
|
66
|
-
|
67
|
-
should 'not modify response when client is ancient, pre-HTTP/1.0' do
|
68
|
-
app = lambda { |env| [200, {"Content-Type" => "text/plain"}, ['Hello', ' ', 'World!']] }
|
69
|
-
check = lambda do
|
70
|
-
status, headers, body = chunked(app).call(@env.dup)
|
71
|
-
status.should.equal 200
|
72
|
-
headers.should.not.include 'Transfer-Encoding'
|
73
|
-
body.join.should.equal 'Hello World!'
|
74
|
-
end
|
75
|
-
|
76
|
-
@env.delete('HTTP_VERSION') # unicorn will do this on pre-HTTP/1.0 requests
|
77
|
-
check.call
|
78
|
-
|
79
|
-
@env['HTTP_VERSION'] = 'HTTP/0.9' # not sure if this happens in practice
|
80
|
-
check.call
|
81
|
-
end
|
82
|
-
|
83
|
-
should 'not modify response when Transfer-Encoding header already present' do
|
84
|
-
app = lambda { |env|
|
85
|
-
[200, {"Content-Type" => "text/plain", 'Transfer-Encoding' => 'identity'}, ['Hello', ' ', 'World!']]
|
86
|
-
}
|
87
|
-
status, headers, body = chunked(app).call(@env)
|
88
|
-
status.should.equal 200
|
89
|
-
headers['Transfer-Encoding'].should.equal 'identity'
|
90
|
-
body.join.should.equal 'Hello World!'
|
91
|
-
end
|
92
|
-
|
93
|
-
[100, 204, 205, 304].each do |status_code|
|
94
|
-
should "not modify response when status code is #{status_code}" do
|
95
|
-
app = lambda { |env| [status_code, {}, []] }
|
96
|
-
status, headers, _ = chunked(app).call(@env)
|
97
|
-
status.should.equal status_code
|
98
|
-
headers.should.not.include 'Transfer-Encoding'
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
data/test/spec_commonlogger.rb
DELETED
@@ -1,93 +0,0 @@
|
|
1
|
-
require 'rack/commonlogger'
|
2
|
-
require 'rack/lint'
|
3
|
-
require 'rack/mock'
|
4
|
-
|
5
|
-
require 'logger'
|
6
|
-
|
7
|
-
describe Rack::CommonLogger do
|
8
|
-
obj = 'foobar'
|
9
|
-
length = obj.size
|
10
|
-
|
11
|
-
app = Rack::Lint.new lambda { |env|
|
12
|
-
[200,
|
13
|
-
{"Content-Type" => "text/html", "Content-Length" => length.to_s},
|
14
|
-
[obj]]}
|
15
|
-
app_without_length = Rack::Lint.new lambda { |env|
|
16
|
-
[200,
|
17
|
-
{"Content-Type" => "text/html"},
|
18
|
-
[]]}
|
19
|
-
app_with_zero_length = Rack::Lint.new lambda { |env|
|
20
|
-
[200,
|
21
|
-
{"Content-Type" => "text/html", "Content-Length" => "0"},
|
22
|
-
[]]}
|
23
|
-
|
24
|
-
should "log to rack.errors by default" do
|
25
|
-
res = Rack::MockRequest.new(Rack::CommonLogger.new(app)).get("/")
|
26
|
-
|
27
|
-
res.errors.should.not.be.empty
|
28
|
-
res.errors.should =~ /"GET \/ " 200 #{length} /
|
29
|
-
end
|
30
|
-
|
31
|
-
should "log to anything with +write+" do
|
32
|
-
log = StringIO.new
|
33
|
-
Rack::MockRequest.new(Rack::CommonLogger.new(app, log)).get("/")
|
34
|
-
|
35
|
-
log.string.should =~ /"GET \/ " 200 #{length} /
|
36
|
-
end
|
37
|
-
|
38
|
-
should "work with standartd library logger" do
|
39
|
-
logdev = StringIO.new
|
40
|
-
log = Logger.new(logdev)
|
41
|
-
Rack::MockRequest.new(Rack::CommonLogger.new(app, log)).get("/")
|
42
|
-
|
43
|
-
logdev.string.should =~ /"GET \/ " 200 #{length} /
|
44
|
-
end
|
45
|
-
|
46
|
-
should "log - content length if header is missing" do
|
47
|
-
res = Rack::MockRequest.new(Rack::CommonLogger.new(app_without_length)).get("/")
|
48
|
-
|
49
|
-
res.errors.should.not.be.empty
|
50
|
-
res.errors.should =~ /"GET \/ " 200 - /
|
51
|
-
end
|
52
|
-
|
53
|
-
should "log - content length if header is zero" do
|
54
|
-
res = Rack::MockRequest.new(Rack::CommonLogger.new(app_with_zero_length)).get("/")
|
55
|
-
|
56
|
-
res.errors.should.not.be.empty
|
57
|
-
res.errors.should =~ /"GET \/ " 200 - /
|
58
|
-
end
|
59
|
-
|
60
|
-
def with_mock_time(t = 0)
|
61
|
-
mc = class <<Time; self; end
|
62
|
-
mc.send :alias_method, :old_now, :now
|
63
|
-
mc.send :define_method, :now do
|
64
|
-
at(t)
|
65
|
-
end
|
66
|
-
yield
|
67
|
-
ensure
|
68
|
-
mc.send :alias_method, :now, :old_now
|
69
|
-
end
|
70
|
-
|
71
|
-
should "log in common log format" do
|
72
|
-
log = StringIO.new
|
73
|
-
with_mock_time do
|
74
|
-
Rack::MockRequest.new(Rack::CommonLogger.new(app, log)).get("/")
|
75
|
-
end
|
76
|
-
|
77
|
-
md = /- - - \[([^\]]+)\] "(\w+) \/ " (\d{3}) \d+ ([\d\.]+)/.match(log.string)
|
78
|
-
md.should.not.equal nil
|
79
|
-
time, method, status, duration = *md.captures
|
80
|
-
time.should.equal Time.at(0).strftime("%d/%b/%Y:%H:%M:%S %z")
|
81
|
-
method.should.equal "GET"
|
82
|
-
status.should.equal "200"
|
83
|
-
(0..1).should.include?(duration.to_f)
|
84
|
-
end
|
85
|
-
|
86
|
-
def length
|
87
|
-
123
|
88
|
-
end
|
89
|
-
|
90
|
-
def self.obj
|
91
|
-
"hello world"
|
92
|
-
end
|
93
|
-
end
|