rack 2.0.8 → 2.2.2
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 +690 -0
- data/CONTRIBUTING.md +136 -0
- data/{COPYING → MIT-LICENSE} +4 -2
- data/README.rdoc +152 -148
- data/Rakefile +37 -23
- data/{SPEC → SPEC.rdoc} +29 -5
- data/bin/rackup +1 -0
- data/example/lobster.ru +2 -0
- data/example/protectedlobster.rb +3 -1
- data/example/protectedlobster.ru +2 -0
- data/lib/rack.rb +67 -73
- data/lib/rack/auth/abstract/handler.rb +3 -1
- data/lib/rack/auth/abstract/request.rb +1 -1
- data/lib/rack/auth/basic.rb +7 -4
- data/lib/rack/auth/digest/md5.rb +13 -11
- data/lib/rack/auth/digest/nonce.rb +6 -3
- data/lib/rack/auth/digest/params.rb +4 -2
- data/lib/rack/auth/digest/request.rb +5 -3
- data/lib/rack/body_proxy.rb +15 -14
- data/lib/rack/builder.rb +116 -23
- data/lib/rack/cascade.rb +28 -12
- data/lib/rack/chunked.rb +68 -20
- data/lib/rack/common_logger.rb +33 -25
- data/lib/rack/conditional_get.rb +20 -16
- data/lib/rack/config.rb +2 -0
- data/lib/rack/content_length.rb +8 -7
- data/lib/rack/content_type.rb +5 -4
- data/lib/rack/core_ext/regexp.rb +14 -0
- data/lib/rack/deflater.rb +59 -34
- data/lib/rack/directory.rb +84 -64
- data/lib/rack/etag.rb +5 -4
- data/lib/rack/events.rb +19 -20
- data/lib/rack/file.rb +4 -173
- data/lib/rack/files.rb +218 -0
- data/lib/rack/handler.rb +7 -2
- data/lib/rack/handler/cgi.rb +2 -3
- data/lib/rack/handler/fastcgi.rb +4 -4
- data/lib/rack/handler/lsws.rb +3 -3
- data/lib/rack/handler/scgi.rb +9 -8
- data/lib/rack/handler/thin.rb +3 -3
- data/lib/rack/handler/webrick.rb +15 -6
- data/lib/rack/head.rb +1 -1
- data/lib/rack/lint.rb +71 -25
- data/lib/rack/lobster.rb +10 -10
- data/lib/rack/lock.rb +2 -1
- data/lib/rack/logger.rb +2 -0
- data/lib/rack/media_type.rb +10 -5
- data/lib/rack/method_override.rb +4 -2
- data/lib/rack/mime.rb +9 -1
- data/lib/rack/mock.rb +97 -20
- data/lib/rack/multipart.rb +6 -4
- data/lib/rack/multipart/generator.rb +17 -13
- data/lib/rack/multipart/parser.rb +54 -56
- data/lib/rack/multipart/uploaded_file.rb +15 -7
- data/lib/rack/null_logger.rb +2 -0
- data/lib/rack/query_parser.rb +53 -28
- data/lib/rack/recursive.rb +7 -5
- data/lib/rack/reloader.rb +8 -4
- data/lib/rack/request.rb +220 -61
- data/lib/rack/response.rb +127 -44
- data/lib/rack/rewindable_input.rb +4 -3
- data/lib/rack/runtime.rb +6 -4
- data/lib/rack/sendfile.rb +13 -9
- data/lib/rack/server.rb +95 -24
- data/lib/rack/session/abstract/id.rb +36 -23
- data/lib/rack/session/cookie.rb +11 -12
- data/lib/rack/session/memcache.rb +4 -93
- data/lib/rack/session/pool.rb +5 -3
- data/lib/rack/show_exceptions.rb +21 -17
- data/lib/rack/show_status.rb +9 -9
- data/lib/rack/static.rb +23 -11
- data/lib/rack/tempfile_reaper.rb +1 -1
- data/lib/rack/urlmap.rb +12 -6
- data/lib/rack/utils.rb +98 -109
- data/lib/rack/version.rb +29 -0
- data/rack.gemspec +40 -28
- metadata +36 -177
- data/HISTORY.md +0 -505
- data/test/builder/an_underscore_app.rb +0 -5
- 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 -9
- data/test/cgi/test.gz +0 -0
- data/test/cgi/test.ru +0 -5
- data/test/gemloader.rb +0 -10
- data/test/helper.rb +0 -34
- 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_encoded_words +0 -7
- 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_single_quote +0 -7
- 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/quoted +0 -15
- data/test/multipart/rack-logo.png +0 -0
- data/test/multipart/semicolon +0 -6
- data/test/multipart/text +0 -15
- data/test/multipart/three_files_three_fields +0 -31
- data/test/multipart/unity3d_wwwform +0 -11
- 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 -89
- data/test/spec_auth_digest.rb +0 -260
- data/test/spec_body_proxy.rb +0 -85
- data/test/spec_builder.rb +0 -233
- data/test/spec_cascade.rb +0 -63
- data/test/spec_cgi.rb +0 -84
- data/test/spec_chunked.rb +0 -103
- data/test/spec_common_logger.rb +0 -95
- data/test/spec_conditional_get.rb +0 -103
- data/test/spec_config.rb +0 -23
- data/test/spec_content_length.rb +0 -86
- data/test/spec_content_type.rb +0 -46
- data/test/spec_deflater.rb +0 -375
- data/test/spec_directory.rb +0 -148
- data/test/spec_etag.rb +0 -108
- data/test/spec_events.rb +0 -133
- data/test/spec_fastcgi.rb +0 -85
- data/test/spec_file.rb +0 -264
- data/test/spec_handler.rb +0 -57
- data/test/spec_head.rb +0 -46
- data/test/spec_lint.rb +0 -515
- data/test/spec_lobster.rb +0 -59
- data/test/spec_lock.rb +0 -204
- data/test/spec_logger.rb +0 -24
- data/test/spec_media_type.rb +0 -42
- data/test/spec_method_override.rb +0 -110
- data/test/spec_mime.rb +0 -51
- data/test/spec_mock.rb +0 -359
- data/test/spec_multipart.rb +0 -722
- data/test/spec_null_logger.rb +0 -21
- data/test/spec_recursive.rb +0 -75
- data/test/spec_request.rb +0 -1407
- data/test/spec_response.rb +0 -510
- data/test/spec_rewindable_input.rb +0 -128
- data/test/spec_runtime.rb +0 -50
- data/test/spec_sendfile.rb +0 -125
- data/test/spec_server.rb +0 -193
- data/test/spec_session_abstract_id.rb +0 -31
- data/test/spec_session_abstract_session_hash.rb +0 -45
- data/test/spec_session_cookie.rb +0 -442
- data/test/spec_session_memcache.rb +0 -357
- data/test/spec_session_pool.rb +0 -247
- data/test/spec_show_exceptions.rb +0 -93
- data/test/spec_show_status.rb +0 -104
- data/test/spec_static.rb +0 -184
- data/test/spec_tempfile_reaper.rb +0 -64
- data/test/spec_thin.rb +0 -96
- data/test/spec_urlmap.rb +0 -237
- data/test/spec_utils.rb +0 -742
- data/test/spec_version.rb +0 -11
- data/test/spec_webrick.rb +0 -206
- data/test/static/another/index.html +0 -1
- data/test/static/foo.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
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rack
|
2
4
|
module Auth
|
3
5
|
# Rack::Auth::AbstractHandler implements common authentication functionality.
|
@@ -8,7 +10,7 @@ module Rack
|
|
8
10
|
|
9
11
|
attr_accessor :realm
|
10
12
|
|
11
|
-
def initialize(app, realm=nil, &authenticator)
|
13
|
+
def initialize(app, realm = nil, &authenticator)
|
12
14
|
@app, @realm, @authenticator = app, realm, authenticator
|
13
15
|
end
|
14
16
|
|
data/lib/rack/auth/basic.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'abstract/handler'
|
4
|
+
require_relative 'abstract/request'
|
5
|
+
require 'base64'
|
3
6
|
|
4
7
|
module Rack
|
5
8
|
module Auth
|
@@ -41,11 +44,11 @@ module Rack
|
|
41
44
|
|
42
45
|
class Request < Auth::AbstractRequest
|
43
46
|
def basic?
|
44
|
-
"basic" == scheme
|
47
|
+
"basic" == scheme && credentials.length == 2
|
45
48
|
end
|
46
49
|
|
47
50
|
def credentials
|
48
|
-
@credentials ||=
|
51
|
+
@credentials ||= Base64.decode64(params).split(':', 2)
|
49
52
|
end
|
50
53
|
|
51
54
|
def username
|
data/lib/rack/auth/digest/md5.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../abstract/handler'
|
4
|
+
require_relative 'request'
|
5
|
+
require_relative 'params'
|
6
|
+
require_relative 'nonce'
|
5
7
|
require 'digest/md5'
|
6
8
|
|
7
9
|
module Rack
|
@@ -21,7 +23,7 @@ module Rack
|
|
21
23
|
|
22
24
|
attr_writer :passwords_hashed
|
23
25
|
|
24
|
-
def initialize(app, realm=nil, opaque=nil, &authenticator)
|
26
|
+
def initialize(app, realm = nil, opaque = nil, &authenticator)
|
25
27
|
@passwords_hashed = nil
|
26
28
|
if opaque.nil? and realm.respond_to? :values_at
|
27
29
|
realm, opaque, @passwords_hashed = realm.values_at :realm, :opaque, :passwords_hashed
|
@@ -47,7 +49,7 @@ module Rack
|
|
47
49
|
|
48
50
|
if valid?(auth)
|
49
51
|
if auth.nonce.stale?
|
50
|
-
return unauthorized(challenge(:
|
52
|
+
return unauthorized(challenge(stale: true))
|
51
53
|
else
|
52
54
|
env['REMOTE_USER'] = auth.username
|
53
55
|
|
@@ -61,7 +63,7 @@ module Rack
|
|
61
63
|
|
62
64
|
private
|
63
65
|
|
64
|
-
QOP = 'auth'
|
66
|
+
QOP = 'auth'
|
65
67
|
|
66
68
|
def params(hash = {})
|
67
69
|
Params.new do |params|
|
@@ -106,21 +108,21 @@ module Rack
|
|
106
108
|
alias :H :md5
|
107
109
|
|
108
110
|
def KD(secret, data)
|
109
|
-
H
|
111
|
+
H "#{secret}:#{data}"
|
110
112
|
end
|
111
113
|
|
112
114
|
def A1(auth, password)
|
113
|
-
|
115
|
+
"#{auth.username}:#{auth.realm}:#{password}"
|
114
116
|
end
|
115
117
|
|
116
118
|
def A2(auth)
|
117
|
-
|
119
|
+
"#{auth.method}:#{auth.uri}"
|
118
120
|
end
|
119
121
|
|
120
122
|
def digest(auth, password)
|
121
123
|
password_hash = passwords_hashed? ? password : H(A1(auth, password))
|
122
124
|
|
123
|
-
KD
|
125
|
+
KD password_hash, "#{auth.nonce}:#{auth.nc}:#{auth.cnonce}:#{QOP}:#{H A2(auth)}"
|
124
126
|
end
|
125
127
|
|
126
128
|
end
|
@@ -1,4 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'digest/md5'
|
4
|
+
require 'base64'
|
2
5
|
|
3
6
|
module Rack
|
4
7
|
module Auth
|
@@ -18,7 +21,7 @@ module Rack
|
|
18
21
|
end
|
19
22
|
|
20
23
|
def self.parse(string)
|
21
|
-
new(*
|
24
|
+
new(*Base64.decode64(string).split(' ', 2))
|
22
25
|
end
|
23
26
|
|
24
27
|
def initialize(timestamp = Time.now, given_digest = nil)
|
@@ -26,11 +29,11 @@ module Rack
|
|
26
29
|
end
|
27
30
|
|
28
31
|
def to_s
|
29
|
-
|
32
|
+
Base64.encode64("#{@timestamp} #{digest}").strip
|
30
33
|
end
|
31
34
|
|
32
35
|
def digest
|
33
|
-
::Digest::MD5.hexdigest(
|
36
|
+
::Digest::MD5.hexdigest("#{@timestamp}:#{self.class.private_key}")
|
34
37
|
end
|
35
38
|
|
36
39
|
def valid?
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rack
|
2
4
|
module Auth
|
3
5
|
module Digest
|
@@ -38,12 +40,12 @@ module Rack
|
|
38
40
|
|
39
41
|
def to_s
|
40
42
|
map do |k, v|
|
41
|
-
"#{k}
|
43
|
+
"#{k}=#{(UNQUOTED.include?(k) ? v.to_s : quote(v))}"
|
42
44
|
end.join(', ')
|
43
45
|
end
|
44
46
|
|
45
47
|
def quote(str) # From WEBrick::HTTPUtils
|
46
|
-
'"'
|
48
|
+
'"' + str.gsub(/[\\\"]/o, "\\\1") + '"'
|
47
49
|
end
|
48
50
|
|
49
51
|
end
|
@@ -1,6 +1,8 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../abstract/request'
|
4
|
+
require_relative 'params'
|
5
|
+
require_relative 'nonce'
|
4
6
|
|
5
7
|
module Rack
|
6
8
|
module Auth
|
data/lib/rack/body_proxy.rb
CHANGED
@@ -1,19 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rack
|
4
|
+
# Proxy for response bodies allowing calling a block when
|
5
|
+
# the response body is closed (after the response has been fully
|
6
|
+
# sent to the client).
|
2
7
|
class BodyProxy
|
8
|
+
# Set the response body to wrap, and the block to call when the
|
9
|
+
# response has been fully sent.
|
3
10
|
def initialize(body, &block)
|
4
11
|
@body = body
|
5
12
|
@block = block
|
6
13
|
@closed = false
|
7
14
|
end
|
8
15
|
|
9
|
-
|
10
|
-
|
11
|
-
when :to_ary, 'to_ary'
|
12
|
-
return false
|
13
|
-
end
|
16
|
+
# Return whether the wrapped body responds to the method.
|
17
|
+
def respond_to_missing?(method_name, include_all = false)
|
14
18
|
super or @body.respond_to?(method_name, include_all)
|
15
19
|
end
|
16
20
|
|
21
|
+
# If not already closed, close the wrapped body and
|
22
|
+
# then call the block the proxy was initialized with.
|
17
23
|
def close
|
18
24
|
return if @closed
|
19
25
|
@closed = true
|
@@ -24,21 +30,16 @@ module Rack
|
|
24
30
|
end
|
25
31
|
end
|
26
32
|
|
33
|
+
# Whether the proxy is closed. The proxy starts as not closed,
|
34
|
+
# and becomes closed on the first call to close.
|
27
35
|
def closed?
|
28
36
|
@closed
|
29
37
|
end
|
30
38
|
|
31
|
-
#
|
32
|
-
# We are applying this special case for #each only. Future bugs of this
|
33
|
-
# class will be handled by requesting users to patch their ruby
|
34
|
-
# implementation, to save adding too many methods in this class.
|
35
|
-
def each
|
36
|
-
@body.each { |body| yield body }
|
37
|
-
end
|
38
|
-
|
39
|
+
# Delegate missing methods to the wrapped body.
|
39
40
|
def method_missing(method_name, *args, &block)
|
40
|
-
super if :to_ary == method_name
|
41
41
|
@body.__send__(method_name, *args, &block)
|
42
42
|
end
|
43
|
+
ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
|
43
44
|
end
|
44
45
|
end
|
data/lib/rack/builder.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rack
|
2
4
|
# Rack::Builder implements a small DSL to iteratively construct Rack
|
3
5
|
# applications.
|
@@ -29,32 +31,102 @@ module Rack
|
|
29
31
|
# You can use +map+ to construct a Rack::URLMap in a convenient way.
|
30
32
|
|
31
33
|
class Builder
|
34
|
+
|
35
|
+
# https://stackoverflow.com/questions/2223882/whats-the-difference-between-utf-8-and-utf-8-without-bom
|
36
|
+
UTF_8_BOM = '\xef\xbb\xbf'
|
37
|
+
|
38
|
+
# Parse the given config file to get a Rack application.
|
39
|
+
#
|
40
|
+
# If the config file ends in +.ru+, it is treated as a
|
41
|
+
# rackup file and the contents will be treated as if
|
42
|
+
# specified inside a Rack::Builder block, using the given
|
43
|
+
# options.
|
44
|
+
#
|
45
|
+
# If the config file does not end in +.ru+, it is
|
46
|
+
# required and Rack will use the basename of the file
|
47
|
+
# to guess which constant will be the Rack application to run.
|
48
|
+
# The options given will be ignored in this case.
|
49
|
+
#
|
50
|
+
# Examples:
|
51
|
+
#
|
52
|
+
# Rack::Builder.parse_file('config.ru')
|
53
|
+
# # Rack application built using Rack::Builder.new
|
54
|
+
#
|
55
|
+
# Rack::Builder.parse_file('app.rb')
|
56
|
+
# # requires app.rb, which can be anywhere in Ruby's
|
57
|
+
# # load path. After requiring, assumes App constant
|
58
|
+
# # contains Rack application
|
59
|
+
#
|
60
|
+
# Rack::Builder.parse_file('./my_app.rb')
|
61
|
+
# # requires ./my_app.rb, which should be in the
|
62
|
+
# # process's current directory. After requiring,
|
63
|
+
# # assumes MyApp constant contains Rack application
|
32
64
|
def self.parse_file(config, opts = Server::Options.new)
|
33
|
-
|
34
|
-
|
35
|
-
cfgfile = ::File.read(config)
|
36
|
-
if cfgfile[/^#\\(.*)/] && opts
|
37
|
-
options = opts.parse! $1.split(/\s+/)
|
38
|
-
end
|
39
|
-
cfgfile.sub!(/^__END__\n.*\Z/m, '')
|
40
|
-
app = new_from_string cfgfile, config
|
65
|
+
if config.end_with?('.ru')
|
66
|
+
return self.load_file(config, opts)
|
41
67
|
else
|
42
68
|
require config
|
43
69
|
app = Object.const_get(::File.basename(config, '.rb').split('_').map(&:capitalize).join(''))
|
70
|
+
return app, {}
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Load the given file as a rackup file, treating the
|
75
|
+
# contents as if specified inside a Rack::Builder block.
|
76
|
+
#
|
77
|
+
# Treats the first comment at the beginning of a line
|
78
|
+
# that starts with a backslash as options similar to
|
79
|
+
# options passed on a rackup command line.
|
80
|
+
#
|
81
|
+
# Ignores content in the file after +__END__+, so that
|
82
|
+
# use of +__END__+ will not result in a syntax error.
|
83
|
+
#
|
84
|
+
# Example config.ru file:
|
85
|
+
#
|
86
|
+
# $ cat config.ru
|
87
|
+
#
|
88
|
+
# #\ -p 9393
|
89
|
+
#
|
90
|
+
# use Rack::ContentLength
|
91
|
+
# require './app.rb'
|
92
|
+
# run App
|
93
|
+
def self.load_file(path, opts = Server::Options.new)
|
94
|
+
options = {}
|
95
|
+
|
96
|
+
cfgfile = ::File.read(path)
|
97
|
+
cfgfile.slice!(/\A#{UTF_8_BOM}/) if cfgfile.encoding == Encoding::UTF_8
|
98
|
+
|
99
|
+
if cfgfile[/^#\\(.*)/] && opts
|
100
|
+
warn "Parsing options from the first comment line is deprecated!"
|
101
|
+
options = opts.parse! $1.split(/\s+/)
|
44
102
|
end
|
103
|
+
|
104
|
+
cfgfile.sub!(/^__END__\n.*\Z/m, '')
|
105
|
+
app = new_from_string cfgfile, path
|
106
|
+
|
45
107
|
return app, options
|
46
108
|
end
|
47
109
|
|
48
|
-
|
49
|
-
|
50
|
-
|
110
|
+
# Evaluate the given +builder_script+ string in the context of
|
111
|
+
# a Rack::Builder block, returning a Rack application.
|
112
|
+
def self.new_from_string(builder_script, file = "(rackup)")
|
113
|
+
# We want to build a variant of TOPLEVEL_BINDING with self as a Rack::Builder instance.
|
114
|
+
# We cannot use instance_eval(String) as that would resolve constants differently.
|
115
|
+
binding, builder = TOPLEVEL_BINDING.eval('Rack::Builder.new.instance_eval { [binding, self] }')
|
116
|
+
eval builder_script, binding, file
|
117
|
+
builder.to_app
|
51
118
|
end
|
52
119
|
|
120
|
+
# Initialize a new Rack::Builder instance. +default_app+ specifies the
|
121
|
+
# default application if +run+ is not called later. If a block
|
122
|
+
# is given, it is evaluted in the context of the instance.
|
53
123
|
def initialize(default_app = nil, &block)
|
54
|
-
@use, @map, @run, @warmup = [], nil, default_app, nil
|
124
|
+
@use, @map, @run, @warmup, @freeze_app = [], nil, default_app, nil, false
|
55
125
|
instance_eval(&block) if block_given?
|
56
126
|
end
|
57
127
|
|
128
|
+
# Create a new Rack::Builder instance and return the Rack application
|
129
|
+
# generated from it.
|
58
130
|
def self.app(default_app = nil, &block)
|
59
131
|
self.new(default_app, &block).to_app
|
60
132
|
end
|
@@ -81,10 +153,11 @@ module Rack
|
|
81
153
|
def use(middleware, *args, &block)
|
82
154
|
if @map
|
83
155
|
mapping, @map = @map, nil
|
84
|
-
@use << proc { |app| generate_map
|
156
|
+
@use << proc { |app| generate_map(app, mapping) }
|
85
157
|
end
|
86
158
|
@use << proc { |app| middleware.new(app, *args, &block) }
|
87
159
|
end
|
160
|
+
ruby2_keywords(:use) if respond_to?(:ruby2_keywords, true)
|
88
161
|
|
89
162
|
# Takes an argument that is an object that responds to #call and returns a Rack response.
|
90
163
|
# The simplest form of this is a lambda object:
|
@@ -104,7 +177,8 @@ module Rack
|
|
104
177
|
@run = app
|
105
178
|
end
|
106
179
|
|
107
|
-
# Takes a lambda or block that is used to warm-up the application.
|
180
|
+
# Takes a lambda or block that is used to warm-up the application. This block is called
|
181
|
+
# before the Rack application is returned by to_app.
|
108
182
|
#
|
109
183
|
# warmup do |app|
|
110
184
|
# client = Rack::MockRequest.new(app)
|
@@ -113,51 +187,70 @@ module Rack
|
|
113
187
|
#
|
114
188
|
# use SomeMiddleware
|
115
189
|
# run MyApp
|
116
|
-
def warmup(prc=nil, &block)
|
190
|
+
def warmup(prc = nil, &block)
|
117
191
|
@warmup = prc || block
|
118
192
|
end
|
119
193
|
|
120
|
-
# Creates a route within the application.
|
194
|
+
# Creates a route within the application. Routes under the mapped path will be sent to
|
195
|
+
# the Rack application specified by run inside the block. Other requests will be sent to the
|
196
|
+
# default application specified by run outside the block.
|
121
197
|
#
|
122
198
|
# Rack::Builder.app do
|
123
|
-
# map '/' do
|
199
|
+
# map '/heartbeat' do
|
124
200
|
# run Heartbeat
|
125
201
|
# end
|
202
|
+
# run App
|
126
203
|
# end
|
127
204
|
#
|
128
|
-
# The +use+ method can also be used
|
205
|
+
# The +use+ method can also be used inside the block to specify middleware to run under a specific path:
|
129
206
|
#
|
130
207
|
# Rack::Builder.app do
|
131
|
-
# map '/' do
|
208
|
+
# map '/heartbeat' do
|
132
209
|
# use Middleware
|
133
210
|
# run Heartbeat
|
134
211
|
# end
|
212
|
+
# run App
|
135
213
|
# end
|
136
214
|
#
|
137
|
-
# This example includes a piece of middleware which will run before requests hit +Heartbeat+.
|
215
|
+
# This example includes a piece of middleware which will run before +/heartbeat+ requests hit +Heartbeat+.
|
138
216
|
#
|
217
|
+
# Note that providing a +path+ of +/+ will ignore any default application given in a +run+ statement
|
218
|
+
# outside the block.
|
139
219
|
def map(path, &block)
|
140
220
|
@map ||= {}
|
141
221
|
@map[path] = block
|
142
222
|
end
|
143
223
|
|
224
|
+
# Freeze the app (set using run) and all middleware instances when building the application
|
225
|
+
# in to_app.
|
226
|
+
def freeze_app
|
227
|
+
@freeze_app = true
|
228
|
+
end
|
229
|
+
|
230
|
+
# Return the Rack application generated by this instance.
|
144
231
|
def to_app
|
145
232
|
app = @map ? generate_map(@run, @map) : @run
|
146
233
|
fail "missing run or map statement" unless app
|
147
|
-
app
|
234
|
+
app.freeze if @freeze_app
|
235
|
+
app = @use.reverse.inject(app) { |a, e| e[a].tap { |x| x.freeze if @freeze_app } }
|
148
236
|
@warmup.call(app) if @warmup
|
149
237
|
app
|
150
238
|
end
|
151
239
|
|
240
|
+
# Call the Rack application generated by this builder instance. Note that
|
241
|
+
# this rebuilds the Rack application and runs the warmup code (if any)
|
242
|
+
# every time it is called, so it should not be used if performance is important.
|
152
243
|
def call(env)
|
153
244
|
to_app.call(env)
|
154
245
|
end
|
155
246
|
|
156
247
|
private
|
157
248
|
|
249
|
+
# Generate a URLMap instance by generating new Rack applications for each
|
250
|
+
# map block in this instance.
|
158
251
|
def generate_map(default_app, mapping)
|
159
|
-
mapped = default_app ? {'/' => default_app} : {}
|
160
|
-
mapping.each { |r,b| mapped[r] = self.class.new(default_app, &b).to_app }
|
252
|
+
mapped = default_app ? { '/' => default_app } : {}
|
253
|
+
mapping.each { |r, b| mapped[r] = self.class.new(default_app, &b).to_app }
|
161
254
|
URLMap.new(mapped)
|
162
255
|
end
|
163
256
|
end
|