rack 2.2.4 → 3.0.8
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 +223 -71
- data/CONTRIBUTING.md +53 -47
- data/MIT-LICENSE +1 -1
- data/README.md +309 -0
- data/SPEC.rdoc +183 -131
- data/lib/rack/auth/abstract/handler.rb +3 -1
- data/lib/rack/auth/abstract/request.rb +3 -1
- data/lib/rack/auth/basic.rb +0 -2
- data/lib/rack/auth/digest/md5.rb +1 -131
- data/lib/rack/auth/digest/nonce.rb +1 -54
- data/lib/rack/auth/digest/params.rb +1 -54
- data/lib/rack/auth/digest/request.rb +1 -43
- data/lib/rack/auth/digest.rb +256 -0
- data/lib/rack/body_proxy.rb +3 -1
- data/lib/rack/builder.rb +83 -63
- data/lib/rack/cascade.rb +2 -0
- data/lib/rack/chunked.rb +16 -13
- data/lib/rack/common_logger.rb +23 -18
- data/lib/rack/conditional_get.rb +18 -15
- data/lib/rack/constants.rb +64 -0
- data/lib/rack/content_length.rb +12 -16
- data/lib/rack/content_type.rb +8 -5
- data/lib/rack/deflater.rb +40 -26
- data/lib/rack/directory.rb +9 -3
- data/lib/rack/etag.rb +14 -23
- data/lib/rack/events.rb +4 -0
- data/lib/rack/file.rb +2 -0
- data/lib/rack/files.rb +15 -17
- data/lib/rack/head.rb +9 -8
- data/lib/rack/headers.rb +154 -0
- data/lib/rack/lint.rb +783 -682
- data/lib/rack/lock.rb +2 -5
- data/lib/rack/logger.rb +2 -0
- data/lib/rack/media_type.rb +1 -1
- data/lib/rack/method_override.rb +6 -2
- data/lib/rack/mime.rb +8 -0
- data/lib/rack/mock.rb +1 -271
- data/lib/rack/mock_request.rb +166 -0
- data/lib/rack/mock_response.rb +126 -0
- data/lib/rack/multipart/generator.rb +7 -5
- data/lib/rack/multipart/parser.rb +134 -65
- data/lib/rack/multipart/uploaded_file.rb +4 -0
- data/lib/rack/multipart.rb +20 -40
- data/lib/rack/null_logger.rb +9 -0
- data/lib/rack/query_parser.rb +78 -46
- data/lib/rack/recursive.rb +2 -0
- data/lib/rack/reloader.rb +0 -2
- data/lib/rack/request.rb +226 -108
- data/lib/rack/response.rb +136 -61
- data/lib/rack/rewindable_input.rb +24 -5
- data/lib/rack/runtime.rb +7 -6
- data/lib/rack/sendfile.rb +30 -25
- data/lib/rack/show_exceptions.rb +15 -2
- data/lib/rack/show_status.rb +17 -7
- data/lib/rack/static.rb +8 -8
- data/lib/rack/tempfile_reaper.rb +15 -4
- data/lib/rack/urlmap.rb +4 -2
- data/lib/rack/utils.rb +223 -185
- data/lib/rack/version.rb +9 -4
- data/lib/rack.rb +6 -76
- metadata +18 -38
- data/README.rdoc +0 -306
- data/Rakefile +0 -130
- data/bin/rackup +0 -5
- data/contrib/rack.png +0 -0
- data/contrib/rack.svg +0 -150
- data/contrib/rack_logo.svg +0 -164
- data/contrib/rdoc.css +0 -412
- data/example/lobster.ru +0 -6
- data/example/protectedlobster.rb +0 -16
- data/example/protectedlobster.ru +0 -10
- data/lib/rack/core_ext/regexp.rb +0 -14
- data/lib/rack/handler/cgi.rb +0 -59
- data/lib/rack/handler/fastcgi.rb +0 -100
- data/lib/rack/handler/lsws.rb +0 -61
- data/lib/rack/handler/scgi.rb +0 -71
- data/lib/rack/handler/thin.rb +0 -36
- data/lib/rack/handler/webrick.rb +0 -129
- data/lib/rack/handler.rb +0 -104
- data/lib/rack/lobster.rb +0 -70
- data/lib/rack/server.rb +0 -466
- data/lib/rack/session/abstract/id.rb +0 -523
- data/lib/rack/session/cookie.rb +0 -203
- data/lib/rack/session/memcache.rb +0 -10
- data/lib/rack/session/pool.rb +0 -85
- data/rack.gemspec +0 -46
@@ -1,54 +1 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
module Rack
|
4
|
-
module Auth
|
5
|
-
module Digest
|
6
|
-
class Params < Hash
|
7
|
-
|
8
|
-
def self.parse(str)
|
9
|
-
Params[*split_header_value(str).map do |param|
|
10
|
-
k, v = param.split('=', 2)
|
11
|
-
[k, dequote(v)]
|
12
|
-
end.flatten]
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.dequote(str) # From WEBrick::HTTPUtils
|
16
|
-
ret = (/\A"(.*)"\Z/ =~ str) ? $1 : str.dup
|
17
|
-
ret.gsub!(/\\(.)/, "\\1")
|
18
|
-
ret
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.split_header_value(str)
|
22
|
-
str.scan(/\w+\=(?:"[^\"]+"|[^,]+)/n)
|
23
|
-
end
|
24
|
-
|
25
|
-
def initialize
|
26
|
-
super()
|
27
|
-
|
28
|
-
yield self if block_given?
|
29
|
-
end
|
30
|
-
|
31
|
-
def [](k)
|
32
|
-
super k.to_s
|
33
|
-
end
|
34
|
-
|
35
|
-
def []=(k, v)
|
36
|
-
super k.to_s, v.to_s
|
37
|
-
end
|
38
|
-
|
39
|
-
UNQUOTED = ['nc', 'stale']
|
40
|
-
|
41
|
-
def to_s
|
42
|
-
map do |k, v|
|
43
|
-
"#{k}=#{(UNQUOTED.include?(k) ? v.to_s : quote(v))}"
|
44
|
-
end.join(', ')
|
45
|
-
end
|
46
|
-
|
47
|
-
def quote(str) # From WEBrick::HTTPUtils
|
48
|
-
'"' + str.gsub(/[\\\"]/o, "\\\1") + '"'
|
49
|
-
end
|
50
|
-
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
1
|
+
require_relative '../digest'
|
@@ -1,43 +1 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require_relative '../abstract/request'
|
4
|
-
require_relative 'params'
|
5
|
-
require_relative 'nonce'
|
6
|
-
|
7
|
-
module Rack
|
8
|
-
module Auth
|
9
|
-
module Digest
|
10
|
-
class Request < Auth::AbstractRequest
|
11
|
-
def method
|
12
|
-
@env[RACK_METHODOVERRIDE_ORIGINAL_METHOD] || @env[REQUEST_METHOD]
|
13
|
-
end
|
14
|
-
|
15
|
-
def digest?
|
16
|
-
"digest" == scheme
|
17
|
-
end
|
18
|
-
|
19
|
-
def correct_uri?
|
20
|
-
request.fullpath == uri
|
21
|
-
end
|
22
|
-
|
23
|
-
def nonce
|
24
|
-
@nonce ||= Nonce.parse(params['nonce'])
|
25
|
-
end
|
26
|
-
|
27
|
-
def params
|
28
|
-
@params ||= Params.parse(parts.last)
|
29
|
-
end
|
30
|
-
|
31
|
-
def respond_to?(sym, *)
|
32
|
-
super or params.has_key? sym.to_s
|
33
|
-
end
|
34
|
-
|
35
|
-
def method_missing(sym, *args)
|
36
|
-
return super unless params.has_key?(key = sym.to_s)
|
37
|
-
return params[key] if args.size == 0
|
38
|
-
raise ArgumentError, "wrong number of arguments (#{args.size} for 0)"
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
1
|
+
require_relative '../digest'
|
@@ -0,0 +1,256 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'abstract/handler'
|
4
|
+
require_relative 'abstract/request'
|
5
|
+
require 'digest/md5'
|
6
|
+
require 'base64'
|
7
|
+
|
8
|
+
module Rack
|
9
|
+
warn "Rack::Auth::Digest is deprecated and will be removed in Rack 3.1", uplevel: 1
|
10
|
+
|
11
|
+
module Auth
|
12
|
+
module Digest
|
13
|
+
# Rack::Auth::Digest::Nonce is the default nonce generator for the
|
14
|
+
# Rack::Auth::Digest::MD5 authentication handler.
|
15
|
+
#
|
16
|
+
# +private_key+ needs to set to a constant string.
|
17
|
+
#
|
18
|
+
# +time_limit+ can be optionally set to an integer (number of seconds),
|
19
|
+
# to limit the validity of the generated nonces.
|
20
|
+
|
21
|
+
class Nonce
|
22
|
+
|
23
|
+
class << self
|
24
|
+
attr_accessor :private_key, :time_limit
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.parse(string)
|
28
|
+
new(*Base64.decode64(string).split(' ', 2))
|
29
|
+
end
|
30
|
+
|
31
|
+
def initialize(timestamp = Time.now, given_digest = nil)
|
32
|
+
@timestamp, @given_digest = timestamp.to_i, given_digest
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_s
|
36
|
+
Base64.encode64("#{@timestamp} #{digest}").strip
|
37
|
+
end
|
38
|
+
|
39
|
+
def digest
|
40
|
+
::Digest::MD5.hexdigest("#{@timestamp}:#{self.class.private_key}")
|
41
|
+
end
|
42
|
+
|
43
|
+
def valid?
|
44
|
+
digest == @given_digest
|
45
|
+
end
|
46
|
+
|
47
|
+
def stale?
|
48
|
+
!self.class.time_limit.nil? && (Time.now.to_i - @timestamp) > self.class.time_limit
|
49
|
+
end
|
50
|
+
|
51
|
+
def fresh?
|
52
|
+
!stale?
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
class Params < Hash
|
58
|
+
|
59
|
+
def self.parse(str)
|
60
|
+
Params[*split_header_value(str).map do |param|
|
61
|
+
k, v = param.split('=', 2)
|
62
|
+
[k, dequote(v)]
|
63
|
+
end.flatten]
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.dequote(str) # From WEBrick::HTTPUtils
|
67
|
+
ret = (/\A"(.*)"\Z/ =~ str) ? $1 : str.dup
|
68
|
+
ret.gsub!(/\\(.)/, "\\1")
|
69
|
+
ret
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.split_header_value(str)
|
73
|
+
str.scan(/\w+\=(?:"[^\"]+"|[^,]+)/n)
|
74
|
+
end
|
75
|
+
|
76
|
+
def initialize
|
77
|
+
super()
|
78
|
+
|
79
|
+
yield self if block_given?
|
80
|
+
end
|
81
|
+
|
82
|
+
def [](k)
|
83
|
+
super k.to_s
|
84
|
+
end
|
85
|
+
|
86
|
+
def []=(k, v)
|
87
|
+
super k.to_s, v.to_s
|
88
|
+
end
|
89
|
+
|
90
|
+
UNQUOTED = ['nc', 'stale']
|
91
|
+
|
92
|
+
def to_s
|
93
|
+
map do |k, v|
|
94
|
+
"#{k}=#{(UNQUOTED.include?(k) ? v.to_s : quote(v))}"
|
95
|
+
end.join(', ')
|
96
|
+
end
|
97
|
+
|
98
|
+
def quote(str) # From WEBrick::HTTPUtils
|
99
|
+
'"' + str.gsub(/[\\\"]/o, "\\\1") + '"'
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
class Request < Auth::AbstractRequest
|
105
|
+
def method
|
106
|
+
@env[RACK_METHODOVERRIDE_ORIGINAL_METHOD] || @env[REQUEST_METHOD]
|
107
|
+
end
|
108
|
+
|
109
|
+
def digest?
|
110
|
+
"digest" == scheme
|
111
|
+
end
|
112
|
+
|
113
|
+
def correct_uri?
|
114
|
+
request.fullpath == uri
|
115
|
+
end
|
116
|
+
|
117
|
+
def nonce
|
118
|
+
@nonce ||= Nonce.parse(params['nonce'])
|
119
|
+
end
|
120
|
+
|
121
|
+
def params
|
122
|
+
@params ||= Params.parse(parts.last)
|
123
|
+
end
|
124
|
+
|
125
|
+
def respond_to?(sym, *)
|
126
|
+
super or params.has_key? sym.to_s
|
127
|
+
end
|
128
|
+
|
129
|
+
def method_missing(sym, *args)
|
130
|
+
return super unless params.has_key?(key = sym.to_s)
|
131
|
+
return params[key] if args.size == 0
|
132
|
+
raise ArgumentError, "wrong number of arguments (#{args.size} for 0)"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# Rack::Auth::Digest::MD5 implements the MD5 algorithm version of
|
137
|
+
# HTTP Digest Authentication, as per RFC 2617.
|
138
|
+
#
|
139
|
+
# Initialize with the [Rack] application that you want protecting,
|
140
|
+
# and a block that looks up a plaintext password for a given username.
|
141
|
+
#
|
142
|
+
# +opaque+ needs to be set to a constant base64/hexadecimal string.
|
143
|
+
#
|
144
|
+
class MD5 < AbstractHandler
|
145
|
+
|
146
|
+
attr_accessor :opaque
|
147
|
+
|
148
|
+
attr_writer :passwords_hashed
|
149
|
+
|
150
|
+
def initialize(app, realm = nil, opaque = nil, &authenticator)
|
151
|
+
@passwords_hashed = nil
|
152
|
+
if opaque.nil? and realm.respond_to? :values_at
|
153
|
+
realm, opaque, @passwords_hashed = realm.values_at :realm, :opaque, :passwords_hashed
|
154
|
+
end
|
155
|
+
super(app, realm, &authenticator)
|
156
|
+
@opaque = opaque
|
157
|
+
end
|
158
|
+
|
159
|
+
def passwords_hashed?
|
160
|
+
!!@passwords_hashed
|
161
|
+
end
|
162
|
+
|
163
|
+
def call(env)
|
164
|
+
auth = Request.new(env)
|
165
|
+
|
166
|
+
unless auth.provided?
|
167
|
+
return unauthorized
|
168
|
+
end
|
169
|
+
|
170
|
+
if !auth.digest? || !auth.correct_uri? || !valid_qop?(auth)
|
171
|
+
return bad_request
|
172
|
+
end
|
173
|
+
|
174
|
+
if valid?(auth)
|
175
|
+
if auth.nonce.stale?
|
176
|
+
return unauthorized(challenge(stale: true))
|
177
|
+
else
|
178
|
+
env['REMOTE_USER'] = auth.username
|
179
|
+
|
180
|
+
return @app.call(env)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
unauthorized
|
185
|
+
end
|
186
|
+
|
187
|
+
|
188
|
+
private
|
189
|
+
|
190
|
+
QOP = 'auth'
|
191
|
+
|
192
|
+
def params(hash = {})
|
193
|
+
Params.new do |params|
|
194
|
+
params['realm'] = realm
|
195
|
+
params['nonce'] = Nonce.new.to_s
|
196
|
+
params['opaque'] = H(opaque)
|
197
|
+
params['qop'] = QOP
|
198
|
+
|
199
|
+
hash.each { |k, v| params[k] = v }
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def challenge(hash = {})
|
204
|
+
"Digest #{params(hash)}"
|
205
|
+
end
|
206
|
+
|
207
|
+
def valid?(auth)
|
208
|
+
valid_opaque?(auth) && valid_nonce?(auth) && valid_digest?(auth)
|
209
|
+
end
|
210
|
+
|
211
|
+
def valid_qop?(auth)
|
212
|
+
QOP == auth.qop
|
213
|
+
end
|
214
|
+
|
215
|
+
def valid_opaque?(auth)
|
216
|
+
H(opaque) == auth.opaque
|
217
|
+
end
|
218
|
+
|
219
|
+
def valid_nonce?(auth)
|
220
|
+
auth.nonce.valid?
|
221
|
+
end
|
222
|
+
|
223
|
+
def valid_digest?(auth)
|
224
|
+
pw = @authenticator.call(auth.username)
|
225
|
+
pw && Rack::Utils.secure_compare(digest(auth, pw), auth.response)
|
226
|
+
end
|
227
|
+
|
228
|
+
def md5(data)
|
229
|
+
::Digest::MD5.hexdigest(data)
|
230
|
+
end
|
231
|
+
|
232
|
+
alias :H :md5
|
233
|
+
|
234
|
+
def KD(secret, data)
|
235
|
+
H "#{secret}:#{data}"
|
236
|
+
end
|
237
|
+
|
238
|
+
def A1(auth, password)
|
239
|
+
"#{auth.username}:#{auth.realm}:#{password}"
|
240
|
+
end
|
241
|
+
|
242
|
+
def A2(auth)
|
243
|
+
"#{auth.method}:#{auth.uri}"
|
244
|
+
end
|
245
|
+
|
246
|
+
def digest(auth, password)
|
247
|
+
password_hash = passwords_hashed? ? password : H(A1(auth, password))
|
248
|
+
|
249
|
+
KD password_hash, "#{auth.nonce}:#{auth.nc}:#{auth.cnonce}:#{QOP}:#{H A2(auth)}"
|
250
|
+
end
|
251
|
+
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
data/lib/rack/body_proxy.rb
CHANGED
@@ -24,7 +24,7 @@ module Rack
|
|
24
24
|
return if @closed
|
25
25
|
@closed = true
|
26
26
|
begin
|
27
|
-
@body.close if @body.respond_to?
|
27
|
+
@body.close if @body.respond_to?(:close)
|
28
28
|
ensure
|
29
29
|
@block.call
|
30
30
|
end
|
@@ -40,6 +40,8 @@ module Rack
|
|
40
40
|
def method_missing(method_name, *args, &block)
|
41
41
|
@body.__send__(method_name, *args, &block)
|
42
42
|
end
|
43
|
+
# :nocov:
|
43
44
|
ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
|
45
|
+
# :nocov:
|
44
46
|
end
|
45
47
|
end
|
data/lib/rack/builder.rb
CHANGED
@@ -1,35 +1,35 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'urlmap'
|
4
|
+
|
3
5
|
module Rack
|
4
|
-
# Rack::Builder
|
5
|
-
# applications.
|
6
|
+
# Rack::Builder provides a domain-specific language (DSL) to construct Rack
|
7
|
+
# applications. It is primarily used to parse +config.ru+ files which
|
8
|
+
# instantiate several middleware and a final application which are hosted
|
9
|
+
# by a Rack-compatible web server.
|
6
10
|
#
|
7
11
|
# Example:
|
8
12
|
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
# run Rack::Lobster.new
|
16
|
-
# end
|
17
|
-
# end
|
13
|
+
# app = Rack::Builder.new do
|
14
|
+
# use Rack::CommonLogger
|
15
|
+
# map "/ok" do
|
16
|
+
# run lambda { |env| [200, {'content-type' => 'text/plain'}, ['OK']] }
|
17
|
+
# end
|
18
|
+
# end
|
18
19
|
#
|
19
|
-
#
|
20
|
+
# run app
|
20
21
|
#
|
21
22
|
# Or
|
22
23
|
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
24
|
+
# app = Rack::Builder.app do
|
25
|
+
# use Rack::CommonLogger
|
26
|
+
# run lambda { |env| [200, {'content-type' => 'text/plain'}, ['OK']] }
|
27
|
+
# end
|
27
28
|
#
|
28
|
-
#
|
29
|
+
# run app
|
29
30
|
#
|
30
31
|
# +use+ adds middleware to the stack, +run+ dispatches to an application.
|
31
32
|
# You can use +map+ to construct a Rack::URLMap in a convenient way.
|
32
|
-
|
33
33
|
class Builder
|
34
34
|
|
35
35
|
# https://stackoverflow.com/questions/2223882/whats-the-difference-between-utf-8-and-utf-8-without-bom
|
@@ -39,13 +39,11 @@ module Rack
|
|
39
39
|
#
|
40
40
|
# If the config file ends in +.ru+, it is treated as a
|
41
41
|
# rackup file and the contents will be treated as if
|
42
|
-
# specified inside a Rack::Builder block
|
43
|
-
# options.
|
42
|
+
# specified inside a Rack::Builder block.
|
44
43
|
#
|
45
44
|
# If the config file does not end in +.ru+, it is
|
46
45
|
# required and Rack will use the basename of the file
|
47
46
|
# to guess which constant will be the Rack application to run.
|
48
|
-
# The options given will be ignored in this case.
|
49
47
|
#
|
50
48
|
# Examples:
|
51
49
|
#
|
@@ -61,23 +59,18 @@ module Rack
|
|
61
59
|
# # requires ./my_app.rb, which should be in the
|
62
60
|
# # process's current directory. After requiring,
|
63
61
|
# # assumes MyApp constant contains Rack application
|
64
|
-
def self.parse_file(
|
65
|
-
if
|
66
|
-
return self.load_file(
|
62
|
+
def self.parse_file(path)
|
63
|
+
if path.end_with?('.ru')
|
64
|
+
return self.load_file(path)
|
67
65
|
else
|
68
|
-
require
|
69
|
-
|
70
|
-
return app, {}
|
66
|
+
require path
|
67
|
+
return Object.const_get(::File.basename(path, '.rb').split('_').map(&:capitalize).join(''))
|
71
68
|
end
|
72
69
|
end
|
73
70
|
|
74
71
|
# Load the given file as a rackup file, treating the
|
75
72
|
# contents as if specified inside a Rack::Builder block.
|
76
73
|
#
|
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
74
|
# Ignores content in the file after +__END__+, so that
|
82
75
|
# use of +__END__+ will not result in a syntax error.
|
83
76
|
#
|
@@ -85,26 +78,20 @@ module Rack
|
|
85
78
|
#
|
86
79
|
# $ cat config.ru
|
87
80
|
#
|
88
|
-
# #\ -p 9393
|
89
|
-
#
|
90
81
|
# use Rack::ContentLength
|
91
82
|
# require './app.rb'
|
92
83
|
# run App
|
93
|
-
def self.load_file(path
|
94
|
-
|
95
|
-
|
96
|
-
cfgfile = ::File.read(path)
|
97
|
-
cfgfile.slice!(/\A#{UTF_8_BOM}/) if cfgfile.encoding == Encoding::UTF_8
|
84
|
+
def self.load_file(path)
|
85
|
+
config = ::File.read(path)
|
86
|
+
config.slice!(/\A#{UTF_8_BOM}/) if config.encoding == Encoding::UTF_8
|
98
87
|
|
99
|
-
if
|
100
|
-
|
101
|
-
options = opts.parse! $1.split(/\s+/)
|
88
|
+
if config[/^#\\(.*)/]
|
89
|
+
fail "Parsing options from the first comment line is no longer supported: #{path}"
|
102
90
|
end
|
103
91
|
|
104
|
-
|
105
|
-
app = new_from_string cfgfile, path
|
92
|
+
config.sub!(/^__END__\n.*\Z/m, '')
|
106
93
|
|
107
|
-
return
|
94
|
+
return new_from_string(config, path)
|
108
95
|
end
|
109
96
|
|
110
97
|
# Evaluate the given +builder_script+ string in the context of
|
@@ -114,14 +101,20 @@ module Rack
|
|
114
101
|
# We cannot use instance_eval(String) as that would resolve constants differently.
|
115
102
|
binding, builder = TOPLEVEL_BINDING.eval('Rack::Builder.new.instance_eval { [binding, self] }')
|
116
103
|
eval builder_script, binding, file
|
117
|
-
|
104
|
+
|
105
|
+
return builder.to_app
|
118
106
|
end
|
119
107
|
|
120
108
|
# Initialize a new Rack::Builder instance. +default_app+ specifies the
|
121
109
|
# default application if +run+ is not called later. If a block
|
122
|
-
# is given, it is
|
110
|
+
# is given, it is evaluated in the context of the instance.
|
123
111
|
def initialize(default_app = nil, &block)
|
124
|
-
@use
|
112
|
+
@use = []
|
113
|
+
@map = nil
|
114
|
+
@run = default_app
|
115
|
+
@warmup = nil
|
116
|
+
@freeze_app = false
|
117
|
+
|
125
118
|
instance_eval(&block) if block_given?
|
126
119
|
end
|
127
120
|
|
@@ -145,7 +138,7 @@ module Rack
|
|
145
138
|
# end
|
146
139
|
#
|
147
140
|
# use Middleware
|
148
|
-
# run lambda { |env| [200, { "
|
141
|
+
# run lambda { |env| [200, { "content-type" => "text/plain" }, ["OK"]] }
|
149
142
|
#
|
150
143
|
# All requests through to this application will first be processed by the middleware class.
|
151
144
|
# The +call+ method in this example sets an additional environment key which then can be
|
@@ -157,24 +150,37 @@ module Rack
|
|
157
150
|
end
|
158
151
|
@use << proc { |app| middleware.new(app, *args, &block) }
|
159
152
|
end
|
153
|
+
# :nocov:
|
160
154
|
ruby2_keywords(:use) if respond_to?(:ruby2_keywords, true)
|
155
|
+
# :nocov:
|
161
156
|
|
162
|
-
# Takes
|
163
|
-
#
|
157
|
+
# Takes a block or argument that is an object that responds to #call and
|
158
|
+
# returns a Rack response.
|
159
|
+
#
|
160
|
+
# You can use a block:
|
161
|
+
#
|
162
|
+
# run do |env|
|
163
|
+
# [200, { "content-type" => "text/plain" }, ["Hello World!"]]
|
164
|
+
# end
|
165
|
+
#
|
166
|
+
# You can also provide a lambda:
|
164
167
|
#
|
165
|
-
# run lambda { |env| [200, { "
|
168
|
+
# run lambda { |env| [200, { "content-type" => "text/plain" }, ["OK"]] }
|
166
169
|
#
|
167
|
-
#
|
170
|
+
# You can also provide a class instance:
|
168
171
|
#
|
169
172
|
# class Heartbeat
|
170
|
-
# def
|
171
|
-
# [200, { "
|
173
|
+
# def call(env)
|
174
|
+
# [200, { "content-type" => "text/plain" }, ["OK"]]
|
172
175
|
# end
|
173
176
|
# end
|
174
177
|
#
|
175
|
-
# run Heartbeat
|
176
|
-
|
177
|
-
|
178
|
+
# run Heartbeat.new
|
179
|
+
#
|
180
|
+
def run(app = nil, &block)
|
181
|
+
raise ArgumentError, "Both app and block given!" if app && block_given?
|
182
|
+
|
183
|
+
@run = app || block
|
178
184
|
end
|
179
185
|
|
180
186
|
# Takes a lambda or block that is used to warm-up the application. This block is called
|
@@ -195,21 +201,35 @@ module Rack
|
|
195
201
|
# the Rack application specified by run inside the block. Other requests will be sent to the
|
196
202
|
# default application specified by run outside the block.
|
197
203
|
#
|
198
|
-
#
|
204
|
+
# class App
|
205
|
+
# def call(env)
|
206
|
+
# [200, {'content-type' => 'text/plain'}, ["Hello World"]]
|
207
|
+
# end
|
208
|
+
# end
|
209
|
+
#
|
210
|
+
# class Heartbeat
|
211
|
+
# def call(env)
|
212
|
+
# [200, { "content-type" => "text/plain" }, ["OK"]]
|
213
|
+
# end
|
214
|
+
# end
|
215
|
+
#
|
216
|
+
# app = Rack::Builder.app do
|
199
217
|
# map '/heartbeat' do
|
200
|
-
# run Heartbeat
|
218
|
+
# run Heartbeat.new
|
201
219
|
# end
|
202
|
-
# run App
|
220
|
+
# run App.new
|
203
221
|
# end
|
204
222
|
#
|
223
|
+
# run app
|
224
|
+
#
|
205
225
|
# The +use+ method can also be used inside the block to specify middleware to run under a specific path:
|
206
226
|
#
|
207
|
-
# Rack::Builder.app do
|
227
|
+
# app = Rack::Builder.app do
|
208
228
|
# map '/heartbeat' do
|
209
229
|
# use Middleware
|
210
|
-
# run Heartbeat
|
230
|
+
# run Heartbeat.new
|
211
231
|
# end
|
212
|
-
# run App
|
232
|
+
# run App.new
|
213
233
|
# end
|
214
234
|
#
|
215
235
|
# This example includes a piece of middleware which will run before +/heartbeat+ requests hit +Heartbeat+.
|
data/lib/rack/cascade.rb
CHANGED
data/lib/rack/chunked.rb
CHANGED
@@ -1,22 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'constants'
|
4
|
+
require_relative 'utils'
|
5
|
+
|
3
6
|
module Rack
|
7
|
+
warn "Rack::Chunked is deprecated and will be removed in Rack 3.1", uplevel: 1
|
4
8
|
|
5
9
|
# Middleware that applies chunked transfer encoding to response bodies
|
6
|
-
# when the response does not include a
|
10
|
+
# when the response does not include a content-length header.
|
7
11
|
#
|
8
|
-
# This supports the
|
12
|
+
# This supports the trailer response header to allow the use of trailing
|
9
13
|
# headers in the chunked encoding. However, using this requires you manually
|
10
14
|
# specify a response body that supports a +trailers+ method. Example:
|
11
15
|
#
|
12
|
-
# [200, { '
|
16
|
+
# [200, { 'trailer' => 'expires'}, ["Hello", "World"]]
|
13
17
|
# # error raised
|
14
18
|
#
|
15
19
|
# body = ["Hello", "World"]
|
16
20
|
# def body.trailers
|
17
|
-
# { '
|
21
|
+
# { 'expires' => Time.now.to_s }
|
18
22
|
# end
|
19
|
-
# [200, { '
|
23
|
+
# [200, { 'trailer' => 'expires'}, body]
|
20
24
|
# # No exception raised
|
21
25
|
class Chunked
|
22
26
|
include Rack::Utils
|
@@ -92,11 +96,10 @@ module Rack
|
|
92
96
|
end
|
93
97
|
|
94
98
|
# If the rack app returns a response that should have a body,
|
95
|
-
# but does not have
|
96
|
-
# modify the response to use chunked
|
99
|
+
# but does not have content-length or transfer-encoding headers,
|
100
|
+
# modify the response to use chunked transfer-encoding.
|
97
101
|
def call(env)
|
98
|
-
status, headers, body = @app.call(env)
|
99
|
-
headers = HeaderHash[headers]
|
102
|
+
status, headers, body = response = @app.call(env)
|
100
103
|
|
101
104
|
if chunkable_version?(env[SERVER_PROTOCOL]) &&
|
102
105
|
!STATUS_WITH_NO_ENTITY_BODY.key?(status.to_i) &&
|
@@ -104,14 +107,14 @@ module Rack
|
|
104
107
|
!headers[TRANSFER_ENCODING]
|
105
108
|
|
106
109
|
headers[TRANSFER_ENCODING] = 'chunked'
|
107
|
-
if headers['
|
108
|
-
|
110
|
+
if headers['trailer']
|
111
|
+
response[2] = TrailerBody.new(body)
|
109
112
|
else
|
110
|
-
|
113
|
+
response[2] = Body.new(body)
|
111
114
|
end
|
112
115
|
end
|
113
116
|
|
114
|
-
|
117
|
+
response
|
115
118
|
end
|
116
119
|
end
|
117
120
|
end
|