rack 1.3.10 → 1.4.0
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.
- data/COPYING +1 -1
- data/KNOWN-ISSUES +0 -9
- data/README.rdoc +4 -118
- data/Rakefile +15 -0
- data/SPEC +3 -5
- data/lib/rack.rb +0 -12
- data/lib/rack/auth/abstract/request.rb +1 -5
- data/lib/rack/auth/basic.rb +1 -1
- data/lib/rack/auth/digest/nonce.rb +1 -1
- data/lib/rack/backports/uri/common_18.rb +28 -14
- data/lib/rack/backports/uri/common_192.rb +17 -14
- data/lib/rack/body_proxy.rb +0 -10
- data/lib/rack/builder.rb +26 -18
- data/lib/rack/cascade.rb +1 -12
- data/lib/rack/chunked.rb +2 -0
- data/lib/rack/content_type.rb +7 -1
- data/lib/rack/deflater.rb +1 -5
- data/lib/rack/directory.rb +5 -1
- data/lib/rack/file.rb +26 -9
- data/lib/rack/handler.rb +2 -2
- data/lib/rack/head.rb +0 -1
- data/lib/rack/lint.rb +3 -5
- data/lib/rack/methodoverride.rb +10 -4
- data/lib/rack/mime.rb +606 -171
- data/lib/rack/mock.rb +2 -1
- data/lib/rack/multipart.rb +2 -2
- data/lib/rack/multipart/parser.rb +3 -10
- data/lib/rack/reloader.rb +1 -1
- data/lib/rack/request.rb +45 -13
- data/lib/rack/response.rb +15 -14
- data/lib/rack/sendfile.rb +8 -6
- data/lib/rack/server.rb +4 -30
- data/lib/rack/session/abstract/id.rb +25 -6
- data/lib/rack/session/cookie.rb +12 -16
- data/lib/rack/static.rb +21 -8
- data/lib/rack/urlmap.rb +28 -13
- data/lib/rack/utils.rb +22 -28
- data/rack.gemspec +5 -5
- data/test/builder/end.ru +2 -0
- data/test/cgi/lighttpd.conf +1 -0
- data/test/cgi/sample_rackup.ru +1 -1
- data/test/cgi/test+directory/test+file +1 -0
- data/test/cgi/test.ru +1 -1
- data/test/gemloader.rb +6 -2
- data/test/spec_auth_basic.rb +4 -9
- data/test/spec_auth_digest.rb +3 -16
- data/test/spec_body_proxy.rb +0 -4
- data/test/spec_builder.rb +63 -20
- data/test/spec_cascade.rb +10 -13
- data/test/spec_cgi.rb +1 -1
- data/test/spec_chunked.rb +39 -12
- data/test/spec_commonlogger.rb +4 -3
- data/test/spec_conditionalget.rb +16 -12
- data/test/spec_content_length.rb +1 -1
- data/test/spec_content_type.rb +6 -0
- data/test/spec_deflater.rb +2 -2
- data/test/spec_directory.rb +12 -0
- data/test/spec_fastcgi.rb +1 -1
- data/test/spec_file.rb +58 -8
- data/test/spec_head.rb +6 -18
- data/test/spec_lint.rb +2 -2
- data/test/spec_methodoverride.rb +15 -0
- data/test/spec_mock.rb +6 -2
- data/test/spec_mongrel.rb +8 -8
- data/test/spec_multipart.rb +10 -63
- data/test/spec_request.rb +94 -21
- data/test/spec_response.rb +22 -24
- data/test/spec_sendfile.rb +3 -0
- data/test/spec_server.rb +2 -49
- data/test/spec_session_cookie.rb +58 -22
- data/test/spec_session_memcache.rb +31 -1
- data/test/spec_session_pool.rb +10 -4
- data/test/spec_static.rb +8 -0
- data/test/spec_thin.rb +2 -2
- data/test/spec_utils.rb +38 -35
- data/test/spec_webrick.rb +5 -3
- data/test/static/index.html +1 -0
- metadata +13 -18
- data/contrib/rack.png +0 -0
- data/contrib/rack.svg +0 -150
- data/lib/rack/backports/uri/common_193.rb +0 -29
- data/test/builder/line.ru +0 -1
- data/test/spec_auth.rb +0 -57
data/lib/rack/static.rb
CHANGED
@@ -22,6 +22,12 @@ module Rack
|
|
22
22
|
#
|
23
23
|
# use Rack::Static, :urls => {"/" => 'index.html'}, :root => 'public'
|
24
24
|
#
|
25
|
+
# Serve all requests normally from the folder "public" in the current
|
26
|
+
# directory but uses index.html as default route for "/"
|
27
|
+
#
|
28
|
+
# use Rack::Static, :urls => [""], :root => 'public', :index =>
|
29
|
+
# 'public/index.html'
|
30
|
+
#
|
25
31
|
# Set a fixed Cache-Control header for all served files:
|
26
32
|
#
|
27
33
|
# use Rack::Static, :root => 'public', :cache_control => 'public'
|
@@ -32,22 +38,29 @@ module Rack
|
|
32
38
|
def initialize(app, options={})
|
33
39
|
@app = app
|
34
40
|
@urls = options[:urls] || ["/favicon.ico"]
|
41
|
+
@index = options[:index] || "index.html"
|
35
42
|
root = options[:root] || Dir.pwd
|
36
43
|
cache_control = options[:cache_control]
|
37
44
|
@file_server = Rack::File.new(root, cache_control)
|
38
45
|
end
|
39
46
|
|
47
|
+
def overwrite_file_path(path)
|
48
|
+
@urls.kind_of?(Hash) && @urls.key?(path) || @index && path == '/'
|
49
|
+
end
|
50
|
+
|
51
|
+
def route_file(path)
|
52
|
+
@urls.kind_of?(Array) && @urls.any? { |url| path.index(url) == 0 }
|
53
|
+
end
|
54
|
+
|
55
|
+
def can_serve(path)
|
56
|
+
route_file(path) || overwrite_file_path(path)
|
57
|
+
end
|
58
|
+
|
40
59
|
def call(env)
|
41
60
|
path = env["PATH_INFO"]
|
42
61
|
|
43
|
-
|
44
|
-
|
45
|
-
else
|
46
|
-
can_serve = @urls.key? path
|
47
|
-
end
|
48
|
-
|
49
|
-
if can_serve
|
50
|
-
env["PATH_INFO"] = @urls[path] if @urls.kind_of? Hash
|
62
|
+
if can_serve(path)
|
63
|
+
env["PATH_INFO"] = (path == '/' ? @index : @urls[path]) if overwrite_file_path(path)
|
51
64
|
@file_server.call(env)
|
52
65
|
else
|
53
66
|
@app.call(env)
|
data/lib/rack/urlmap.rb
CHANGED
@@ -19,9 +19,6 @@ module Rack
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def remap(map)
|
22
|
-
longest_path_first = lambda do |(host, location, _, _)|
|
23
|
-
[host ? -host.size : NEGATIVE_INFINITY, -location.size]
|
24
|
-
end
|
25
22
|
@mapping = map.map { |location, app|
|
26
23
|
if location =~ %r{\Ahttps?://(.*?)(/.*)}
|
27
24
|
host, location = $1, $2
|
@@ -32,28 +29,46 @@ module Rack
|
|
32
29
|
unless location[0] == ?/
|
33
30
|
raise ArgumentError, "paths need to start with /"
|
34
31
|
end
|
32
|
+
|
35
33
|
location = location.chomp('/')
|
36
34
|
match = Regexp.new("^#{Regexp.quote(location).gsub('/', '/+')}(.*)", nil, 'n')
|
37
35
|
|
38
36
|
[host, location, match, app]
|
39
|
-
}.sort_by(
|
37
|
+
}.sort_by do |(host, location, _, _)|
|
38
|
+
[host ? -host.size : NEGATIVE_INFINITY, -location.size]
|
39
|
+
end
|
40
40
|
end
|
41
41
|
|
42
42
|
def call(env)
|
43
43
|
path = env["PATH_INFO"]
|
44
44
|
script_name = env['SCRIPT_NAME']
|
45
|
-
hHost
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
45
|
+
hHost = env['HTTP_HOST']
|
46
|
+
sName = env['SERVER_NAME']
|
47
|
+
sPort = env['SERVER_PORT']
|
48
|
+
|
49
|
+
@mapping.each do |host, location, match, app|
|
50
|
+
unless hHost == host \
|
51
|
+
|| sName == host \
|
52
|
+
|| (!host && (hHost == sName || hHost == sName+':'+sPort))
|
53
|
+
next
|
54
|
+
end
|
55
|
+
|
56
|
+
next unless m = match.match(path.to_s)
|
57
|
+
|
58
|
+
rest = m[1]
|
59
|
+
next unless !rest || rest.empty? || rest[0] == ?/
|
60
|
+
|
61
|
+
env['SCRIPT_NAME'] = (script_name + location)
|
62
|
+
env['PATH_INFO'] = rest
|
63
|
+
|
52
64
|
return app.call(env)
|
53
|
-
|
65
|
+
end
|
66
|
+
|
54
67
|
[404, {"Content-Type" => "text/plain", "X-Cascade" => "pass"}, ["Not Found: #{path}"]]
|
68
|
+
|
55
69
|
ensure
|
56
|
-
env
|
70
|
+
env['PATH_INFO'] = path
|
71
|
+
env['SCRIPT_NAME'] = script_name
|
57
72
|
end
|
58
73
|
end
|
59
74
|
end
|
data/lib/rack/utils.rb
CHANGED
@@ -5,14 +5,11 @@ require 'tempfile'
|
|
5
5
|
require 'rack/multipart'
|
6
6
|
|
7
7
|
major, minor, patch = RUBY_VERSION.split('.').map { |v| v.to_i }
|
8
|
-
ruby_engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby'
|
9
8
|
|
10
9
|
if major == 1 && minor < 9
|
11
10
|
require 'rack/backports/uri/common_18'
|
12
|
-
elsif major == 1 && minor == 9 && patch
|
11
|
+
elsif major == 1 && minor == 9 && patch < 3
|
13
12
|
require 'rack/backports/uri/common_192'
|
14
|
-
elsif major == 1 && minor == 9 && patch == 3 && RUBY_PATCHLEVEL < 125
|
15
|
-
require 'rack/backports/uri/common_193'
|
16
13
|
else
|
17
14
|
require 'uri/common'
|
18
15
|
end
|
@@ -35,9 +32,16 @@ module Rack
|
|
35
32
|
end
|
36
33
|
module_function :escape_path
|
37
34
|
|
38
|
-
# Unescapes a URI escaped string
|
39
|
-
|
40
|
-
|
35
|
+
# Unescapes a URI escaped string with +encoding+. +encoding+ will be the
|
36
|
+
# target encoding of the string returned, and it defaults to UTF-8
|
37
|
+
if defined?(::Encoding)
|
38
|
+
def unescape(s, encoding = Encoding::UTF_8)
|
39
|
+
URI.decode_www_form_component(s, encoding)
|
40
|
+
end
|
41
|
+
else
|
42
|
+
def unescape(s, encoding = nil)
|
43
|
+
URI.decode_www_form_component(s, encoding)
|
44
|
+
end
|
41
45
|
end
|
42
46
|
module_function :unescape
|
43
47
|
|
@@ -63,7 +67,6 @@ module Rack
|
|
63
67
|
bytes = 0
|
64
68
|
|
65
69
|
(qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p|
|
66
|
-
next if p.empty?
|
67
70
|
k, v = p.split('=', 2).map { |x| unescape(x) }
|
68
71
|
|
69
72
|
if k
|
@@ -148,7 +151,7 @@ module Rack
|
|
148
151
|
if v.class == Array
|
149
152
|
build_query(v.map { |x| [k, x] })
|
150
153
|
else
|
151
|
-
"#{escape(k)}=#{escape(v)}"
|
154
|
+
v.nil? ? escape(k) : "#{escape(k)}=#{escape(v)}"
|
152
155
|
end
|
153
156
|
}.join("&")
|
154
157
|
end
|
@@ -269,6 +272,8 @@ module Rack
|
|
269
272
|
cookies.reject! { |cookie|
|
270
273
|
if value[:domain]
|
271
274
|
cookie =~ /\A#{escape(key)}=.*domain=#{value[:domain]}/
|
275
|
+
elsif value[:path]
|
276
|
+
cookie =~ /\A#{escape(key)}=.*path=#{value[:path]}/
|
272
277
|
else
|
273
278
|
cookie =~ /\A#{escape(key)}=/
|
274
279
|
end
|
@@ -319,16 +324,16 @@ module Rack
|
|
319
324
|
def byte_ranges(env, size)
|
320
325
|
# See <http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35>
|
321
326
|
http_range = env['HTTP_RANGE']
|
322
|
-
return nil unless http_range
|
327
|
+
return nil unless http_range
|
323
328
|
ranges = []
|
324
|
-
|
325
|
-
|
326
|
-
|
329
|
+
http_range.split(/,\s*/).each do |range_spec|
|
330
|
+
matches = range_spec.match(/bytes=(\d*)-(\d*)/)
|
331
|
+
return nil unless matches
|
332
|
+
r0,r1 = matches[1], matches[2]
|
327
333
|
if r0.empty?
|
328
334
|
return nil if r1.empty?
|
329
335
|
# suffix-byte-range-spec, represents trailing suffix of file
|
330
|
-
r0 = size - r1.to_i
|
331
|
-
r0 = 0 if r0 < 0
|
336
|
+
r0 = [size - r1.to_i, 0].max
|
332
337
|
r1 = size - 1
|
333
338
|
else
|
334
339
|
r0 = r0.to_i
|
@@ -346,18 +351,6 @@ module Rack
|
|
346
351
|
end
|
347
352
|
module_function :byte_ranges
|
348
353
|
|
349
|
-
# Constant time string comparison.
|
350
|
-
def secure_compare(a, b)
|
351
|
-
return false unless bytesize(a) == bytesize(b)
|
352
|
-
|
353
|
-
l = a.unpack("C*")
|
354
|
-
|
355
|
-
r, i = 0, -1
|
356
|
-
b.each_byte { |v| r |= v ^ l[i+=1] }
|
357
|
-
r == 0
|
358
|
-
end
|
359
|
-
module_function :secure_compare
|
360
|
-
|
361
354
|
# Context allows the use of a compatible middleware at different points
|
362
355
|
# in a request handling stack. A compatible middleware must define
|
363
356
|
# #context which should take the arguments env and app. The first of which
|
@@ -496,6 +489,7 @@ module Rack
|
|
496
489
|
415 => 'Unsupported Media Type',
|
497
490
|
416 => 'Requested Range Not Satisfiable',
|
498
491
|
417 => 'Expectation Failed',
|
492
|
+
418 => "I'm a Teapot",
|
499
493
|
422 => 'Unprocessable Entity',
|
500
494
|
423 => 'Locked',
|
501
495
|
424 => 'Failed Dependency',
|
@@ -512,7 +506,7 @@ module Rack
|
|
512
506
|
}
|
513
507
|
|
514
508
|
# Responses with HTTP status codes that should not have an entity body
|
515
|
-
STATUS_WITH_NO_ENTITY_BODY = Set.new((100..199).to_a << 204 << 304)
|
509
|
+
STATUS_WITH_NO_ENTITY_BODY = Set.new((100..199).to_a << 204 << 205 << 304)
|
516
510
|
|
517
511
|
SYMBOL_TO_STATUS_CODE = Hash[*HTTP_STATUS_CODES.map { |code, message|
|
518
512
|
[message.downcase.gsub(/\s|-/, '_').to_sym, code]
|
data/rack.gemspec
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "rack"
|
3
|
-
s.version = "1.
|
3
|
+
s.version = "1.4.0"
|
4
4
|
s.platform = Gem::Platform::RUBY
|
5
5
|
s.summary = "a modular Ruby webserver interface"
|
6
6
|
|
7
7
|
s.description = <<-EOF
|
8
|
-
Rack provides minimal, modular and adaptable interface for developing
|
8
|
+
Rack provides a minimal, modular and adaptable interface for developing
|
9
9
|
web applications in Ruby. By wrapping HTTP requests and responses in
|
10
10
|
the simplest way possible, it unifies and distills the API for web
|
11
11
|
servers, web frameworks, and software in between (the so-called
|
12
12
|
middleware) into a single method call.
|
13
13
|
|
14
|
-
Also see http://rack.
|
14
|
+
Also see http://rack.rubyforge.org.
|
15
15
|
EOF
|
16
16
|
|
17
17
|
s.files = Dir['{bin/*,contrib/*,example/*,lib/**/*,test/**/*}'] +
|
@@ -24,13 +24,13 @@ EOF
|
|
24
24
|
|
25
25
|
s.author = 'Christian Neukirchen'
|
26
26
|
s.email = 'chneukirchen@gmail.com'
|
27
|
-
s.homepage = 'http://rack.
|
27
|
+
s.homepage = 'http://rack.rubyforge.org'
|
28
28
|
s.rubyforge_project = 'rack'
|
29
29
|
|
30
30
|
s.add_development_dependency 'bacon'
|
31
31
|
s.add_development_dependency 'rake'
|
32
32
|
|
33
|
-
s.add_development_dependency 'fcgi'
|
33
|
+
s.add_development_dependency 'ruby-fcgi'
|
34
34
|
s.add_development_dependency 'memcache-client'
|
35
35
|
s.add_development_dependency 'mongrel', '>= 1.2.0.pre2'
|
36
36
|
s.add_development_dependency 'thin'
|
data/test/builder/end.ru
CHANGED
data/test/cgi/lighttpd.conf
CHANGED
data/test/cgi/sample_rackup.ru
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
this file has plusses!
|
data/test/cgi/test.ru
CHANGED
data/test/gemloader.rb
CHANGED
@@ -2,5 +2,9 @@ require 'rubygems'
|
|
2
2
|
project = 'rack'
|
3
3
|
gemspec = File.expand_path("#{project}.gemspec", Dir.pwd)
|
4
4
|
Gem::Specification.load(gemspec).dependencies.each do |dep|
|
5
|
-
|
6
|
-
|
5
|
+
begin
|
6
|
+
gem dep.name, *dep.requirement.as_list
|
7
|
+
rescue Gem::LoadError
|
8
|
+
warn "Cannot load #{dep.name} #{dep.requirement.to_s}"
|
9
|
+
end
|
10
|
+
end
|
data/test/spec_auth_basic.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'rack/auth/basic'
|
2
|
+
require 'rack/lint'
|
2
3
|
require 'rack/mock'
|
3
4
|
|
4
5
|
describe Rack::Auth::Basic do
|
@@ -7,7 +8,9 @@ describe Rack::Auth::Basic do
|
|
7
8
|
end
|
8
9
|
|
9
10
|
def unprotected_app
|
10
|
-
lambda { |env|
|
11
|
+
Rack::Lint.new lambda { |env|
|
12
|
+
[ 200, {'Content-Type' => 'text/plain'}, ["Hi #{env['REMOTE_USER']}"] ]
|
13
|
+
}
|
11
14
|
end
|
12
15
|
|
13
16
|
def protected_app
|
@@ -63,14 +66,6 @@ describe Rack::Auth::Basic do
|
|
63
66
|
end
|
64
67
|
end
|
65
68
|
|
66
|
-
should 'return 400 Bad Request for a malformed authorization header' do
|
67
|
-
request 'HTTP_AUTHORIZATION' => '' do |response|
|
68
|
-
response.should.be.a.client_error
|
69
|
-
response.status.should.equal 400
|
70
|
-
response.should.not.include 'WWW-Authenticate'
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
69
|
it 'takes realm as optional constructor arg' do
|
75
70
|
app = Rack::Auth::Basic.new(unprotected_app, realm) { true }
|
76
71
|
realm.should == app.realm
|
data/test/spec_auth_digest.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'rack/auth/digest/md5'
|
2
|
+
require 'rack/lint'
|
2
3
|
require 'rack/mock'
|
3
4
|
|
4
5
|
describe Rack::Auth::Digest::MD5 do
|
@@ -7,10 +8,10 @@ describe Rack::Auth::Digest::MD5 do
|
|
7
8
|
end
|
8
9
|
|
9
10
|
def unprotected_app
|
10
|
-
lambda
|
11
|
+
Rack::Lint.new lambda { |env|
|
11
12
|
friend = Rack::Utils.parse_query(env["QUERY_STRING"])["friend"]
|
12
13
|
[ 200, {'Content-Type' => 'text/plain'}, ["Hi #{env['REMOTE_USER']}#{friend ? " and #{friend}" : ''}"] ]
|
13
|
-
|
14
|
+
}
|
14
15
|
end
|
15
16
|
|
16
17
|
def protected_app
|
@@ -152,20 +153,6 @@ describe Rack::Auth::Digest::MD5 do
|
|
152
153
|
end
|
153
154
|
end
|
154
155
|
|
155
|
-
should 'not rechallenge if nonce is not stale' do
|
156
|
-
begin
|
157
|
-
Rack::Auth::Digest::Nonce.time_limit = 10
|
158
|
-
|
159
|
-
request_with_digest_auth 'GET', '/', 'Alice', 'correct-password', :wait => 1 do |response|
|
160
|
-
response.status.should.equal 200
|
161
|
-
response.body.to_s.should.equal 'Hi Alice'
|
162
|
-
response.headers['WWW-Authenticate'].should.not =~ /\bstale=true\b/
|
163
|
-
end
|
164
|
-
ensure
|
165
|
-
Rack::Auth::Digest::Nonce.time_limit = nil
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
156
|
should 'rechallenge with stale parameter if nonce is stale' do
|
170
157
|
begin
|
171
158
|
Rack::Auth::Digest::Nonce.time_limit = 1
|
data/test/spec_body_proxy.rb
CHANGED
data/test/spec_builder.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'rack/builder'
|
2
|
+
require 'rack/lint'
|
2
3
|
require 'rack/mock'
|
3
4
|
require 'rack/showexceptions'
|
4
5
|
require 'rack/urlmap'
|
@@ -18,38 +19,46 @@ class NothingMiddleware
|
|
18
19
|
end
|
19
20
|
|
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
|
+
|
21
30
|
it "supports mapping" do
|
22
|
-
app =
|
31
|
+
app = builder_to_app do
|
23
32
|
map '/' do |outer_env|
|
24
|
-
run lambda { |inner_env| [200, {}, ['root']] }
|
33
|
+
run lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, ['root']] }
|
25
34
|
end
|
26
35
|
map '/sub' do
|
27
|
-
run lambda { |inner_env| [200, {}, ['sub']] }
|
36
|
+
run lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, ['sub']] }
|
28
37
|
end
|
29
|
-
end
|
38
|
+
end
|
30
39
|
Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'root'
|
31
40
|
Rack::MockRequest.new(app).get("/sub").body.to_s.should.equal 'sub'
|
32
41
|
end
|
33
42
|
|
34
43
|
it "doesn't dupe env even when mapping" do
|
35
|
-
app =
|
44
|
+
app = builder_to_app do
|
36
45
|
use NothingMiddleware
|
37
46
|
map '/' do |outer_env|
|
38
47
|
run lambda { |inner_env|
|
39
48
|
inner_env['new_key'] = 'new_value'
|
40
|
-
[200, {}, ['root']]
|
49
|
+
[200, {"Content-Type" => "text/plain"}, ['root']]
|
41
50
|
}
|
42
51
|
end
|
43
|
-
end
|
52
|
+
end
|
44
53
|
Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'root'
|
45
54
|
NothingMiddleware.env['new_key'].should.equal 'new_value'
|
46
55
|
end
|
47
56
|
|
48
57
|
it "chains apps by default" do
|
49
|
-
app =
|
58
|
+
app = builder_to_app do
|
50
59
|
use Rack::ShowExceptions
|
51
60
|
run lambda { |env| raise "bzzzt" }
|
52
|
-
end
|
61
|
+
end
|
53
62
|
|
54
63
|
Rack::MockRequest.new(app).get("/").should.be.server_error
|
55
64
|
Rack::MockRequest.new(app).get("/").should.be.server_error
|
@@ -57,7 +66,7 @@ describe Rack::Builder do
|
|
57
66
|
end
|
58
67
|
|
59
68
|
it "has implicit #to_app" do
|
60
|
-
app =
|
69
|
+
app = builder do
|
61
70
|
use Rack::ShowExceptions
|
62
71
|
run lambda { |env| raise "bzzzt" }
|
63
72
|
end
|
@@ -68,13 +77,13 @@ describe Rack::Builder do
|
|
68
77
|
end
|
69
78
|
|
70
79
|
it "supports blocks on use" do
|
71
|
-
app =
|
80
|
+
app = builder do
|
72
81
|
use Rack::ShowExceptions
|
73
82
|
use Rack::Auth::Basic do |username, password|
|
74
83
|
'secret' == password
|
75
84
|
end
|
76
85
|
|
77
|
-
run lambda { |env| [200, {}, ['Hi Boss']] }
|
86
|
+
run lambda { |env| [200, {"Content-Type" => "text/plain"}, ['Hi Boss']] }
|
78
87
|
end
|
79
88
|
|
80
89
|
response = Rack::MockRequest.new(app).get("/")
|
@@ -89,7 +98,7 @@ describe Rack::Builder do
|
|
89
98
|
end
|
90
99
|
|
91
100
|
it "has explicit #to_app" do
|
92
|
-
app =
|
101
|
+
app = builder do
|
93
102
|
use Rack::ShowExceptions
|
94
103
|
run lambda { |env| raise "bzzzt" }
|
95
104
|
end
|
@@ -99,8 +108,30 @@ describe Rack::Builder do
|
|
99
108
|
Rack::MockRequest.new(app).get("/").should.be.server_error
|
100
109
|
end
|
101
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
|
+
|
102
133
|
should "initialize apps once" do
|
103
|
-
app =
|
134
|
+
app = builder do
|
104
135
|
class AppClass
|
105
136
|
def initialize
|
106
137
|
@called = 0
|
@@ -120,6 +151,23 @@ describe Rack::Builder do
|
|
120
151
|
Rack::MockRequest.new(app).get("/").should.be.server_error
|
121
152
|
end
|
122
153
|
|
154
|
+
it "allows use after run" do
|
155
|
+
app = builder do
|
156
|
+
run lambda { |env| raise "bzzzt" }
|
157
|
+
use Rack::ShowExceptions
|
158
|
+
end
|
159
|
+
|
160
|
+
Rack::MockRequest.new(app).get("/").should.be.server_error
|
161
|
+
Rack::MockRequest.new(app).get("/").should.be.server_error
|
162
|
+
Rack::MockRequest.new(app).get("/").should.be.server_error
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'complains about a missing run' do
|
166
|
+
proc do
|
167
|
+
Rack::Lint.new Rack::Builder.app { use Rack::ShowExceptions }
|
168
|
+
end.should.raise(RuntimeError)
|
169
|
+
end
|
170
|
+
|
123
171
|
describe "parse_file" do
|
124
172
|
def config_file(name)
|
125
173
|
File.join(File.dirname(__FILE__), 'builder', name)
|
@@ -133,6 +181,7 @@ describe Rack::Builder do
|
|
133
181
|
|
134
182
|
it "removes __END__ before evaluating app" do
|
135
183
|
app, options = Rack::Builder.parse_file config_file('end.ru')
|
184
|
+
options = nil # ignored, prevents warning
|
136
185
|
Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'OK'
|
137
186
|
end
|
138
187
|
|
@@ -148,11 +197,5 @@ describe Rack::Builder do
|
|
148
197
|
Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'OK'
|
149
198
|
$:.pop
|
150
199
|
end
|
151
|
-
|
152
|
-
it "sets __LINE__ correctly" do
|
153
|
-
app, options = Rack::Builder.parse_file config_file('line.ru')
|
154
|
-
options = nil # ignored, prevents warning
|
155
|
-
Rack::MockRequest.new(app).get("/").body.to_s.should.equal '1'
|
156
|
-
end
|
157
200
|
end
|
158
201
|
end
|