rack 2.2.8 → 3.0.9

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.

Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +213 -83
  3. data/CONTRIBUTING.md +53 -47
  4. data/MIT-LICENSE +1 -1
  5. data/README.md +309 -0
  6. data/SPEC.rdoc +174 -126
  7. data/lib/rack/auth/abstract/handler.rb +3 -1
  8. data/lib/rack/auth/abstract/request.rb +3 -1
  9. data/lib/rack/auth/basic.rb +0 -2
  10. data/lib/rack/auth/digest/md5.rb +1 -131
  11. data/lib/rack/auth/digest/nonce.rb +1 -54
  12. data/lib/rack/auth/digest/params.rb +1 -54
  13. data/lib/rack/auth/digest/request.rb +1 -43
  14. data/lib/rack/auth/digest.rb +256 -0
  15. data/lib/rack/body_proxy.rb +3 -1
  16. data/lib/rack/builder.rb +83 -63
  17. data/lib/rack/cascade.rb +2 -0
  18. data/lib/rack/chunked.rb +16 -13
  19. data/lib/rack/common_logger.rb +23 -18
  20. data/lib/rack/conditional_get.rb +18 -15
  21. data/lib/rack/constants.rb +64 -0
  22. data/lib/rack/content_length.rb +12 -16
  23. data/lib/rack/content_type.rb +8 -5
  24. data/lib/rack/deflater.rb +40 -26
  25. data/lib/rack/directory.rb +9 -3
  26. data/lib/rack/etag.rb +14 -23
  27. data/lib/rack/events.rb +4 -0
  28. data/lib/rack/file.rb +2 -0
  29. data/lib/rack/files.rb +15 -17
  30. data/lib/rack/head.rb +9 -8
  31. data/lib/rack/headers.rb +154 -0
  32. data/lib/rack/lint.rb +758 -646
  33. data/lib/rack/lock.rb +2 -5
  34. data/lib/rack/logger.rb +2 -0
  35. data/lib/rack/media_type.rb +1 -1
  36. data/lib/rack/method_override.rb +5 -1
  37. data/lib/rack/mime.rb +8 -0
  38. data/lib/rack/mock.rb +1 -271
  39. data/lib/rack/mock_request.rb +166 -0
  40. data/lib/rack/mock_response.rb +126 -0
  41. data/lib/rack/multipart/generator.rb +7 -5
  42. data/lib/rack/multipart/parser.rb +120 -64
  43. data/lib/rack/multipart/uploaded_file.rb +4 -0
  44. data/lib/rack/multipart.rb +20 -40
  45. data/lib/rack/null_logger.rb +9 -0
  46. data/lib/rack/query_parser.rb +78 -46
  47. data/lib/rack/recursive.rb +2 -0
  48. data/lib/rack/reloader.rb +0 -2
  49. data/lib/rack/request.rb +224 -106
  50. data/lib/rack/response.rb +138 -61
  51. data/lib/rack/rewindable_input.rb +24 -5
  52. data/lib/rack/runtime.rb +7 -6
  53. data/lib/rack/sendfile.rb +30 -25
  54. data/lib/rack/show_exceptions.rb +15 -2
  55. data/lib/rack/show_status.rb +17 -7
  56. data/lib/rack/static.rb +8 -8
  57. data/lib/rack/tempfile_reaper.rb +15 -4
  58. data/lib/rack/urlmap.rb +3 -1
  59. data/lib/rack/utils.rb +202 -176
  60. data/lib/rack/version.rb +9 -4
  61. data/lib/rack.rb +6 -76
  62. metadata +15 -35
  63. data/README.rdoc +0 -320
  64. data/Rakefile +0 -130
  65. data/bin/rackup +0 -5
  66. data/contrib/rack.png +0 -0
  67. data/contrib/rack.svg +0 -150
  68. data/contrib/rack_logo.svg +0 -164
  69. data/contrib/rdoc.css +0 -412
  70. data/example/lobster.ru +0 -6
  71. data/example/protectedlobster.rb +0 -16
  72. data/example/protectedlobster.ru +0 -10
  73. data/lib/rack/core_ext/regexp.rb +0 -14
  74. data/lib/rack/handler/cgi.rb +0 -59
  75. data/lib/rack/handler/fastcgi.rb +0 -100
  76. data/lib/rack/handler/lsws.rb +0 -61
  77. data/lib/rack/handler/scgi.rb +0 -71
  78. data/lib/rack/handler/thin.rb +0 -36
  79. data/lib/rack/handler/webrick.rb +0 -129
  80. data/lib/rack/handler.rb +0 -104
  81. data/lib/rack/lobster.rb +0 -70
  82. data/lib/rack/server.rb +0 -466
  83. data/lib/rack/session/abstract/id.rb +0 -523
  84. data/lib/rack/session/cookie.rb +0 -204
  85. data/lib/rack/session/memcache.rb +0 -10
  86. data/lib/rack/session/pool.rb +0 -85
  87. data/rack.gemspec +0 -46
@@ -1,54 +1 @@
1
- # frozen_string_literal: true
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
- # frozen_string_literal: true
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
+
@@ -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? :close
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 implements a small DSL to iteratively construct Rack
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
- # require 'rack/lobster'
10
- # app = Rack::Builder.new do
11
- # use Rack::CommonLogger
12
- # use Rack::ShowExceptions
13
- # map "/lobster" do
14
- # use Rack::Lint
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
- # run app
20
+ # run app
20
21
  #
21
22
  # Or
22
23
  #
23
- # app = Rack::Builder.app do
24
- # use Rack::CommonLogger
25
- # run lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['OK']] }
26
- # end
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
- # run app
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, using the given
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(config, opts = Server::Options.new)
65
- if config.end_with?('.ru')
66
- return self.load_file(config, opts)
62
+ def self.parse_file(path)
63
+ if path.end_with?('.ru')
64
+ return self.load_file(path)
67
65
  else
68
- require config
69
- app = Object.const_get(::File.basename(config, '.rb').split('_').map(&:capitalize).join(''))
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, 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
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 cfgfile[/^#\\(.*)/] && opts
100
- warn "Parsing options from the first comment line is deprecated!"
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
- cfgfile.sub!(/^__END__\n.*\Z/m, '')
105
- app = new_from_string cfgfile, path
92
+ config.sub!(/^__END__\n.*\Z/m, '')
106
93
 
107
- return app, options
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
- builder.to_app
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 evaluted in the context of the instance.
110
+ # is given, it is evaluated in the context of the instance.
123
111
  def initialize(default_app = nil, &block)
124
- @use, @map, @run, @warmup, @freeze_app = [], nil, default_app, nil, false
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, { "Content-Type" => "text/plain" }, ["OK"]] }
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 an argument that is an object that responds to #call and returns a Rack response.
163
- # The simplest form of this is a lambda object:
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, { "Content-Type" => "text/plain" }, ["OK"]] }
168
+ # run lambda { |env| [200, { "content-type" => "text/plain" }, ["OK"]] }
166
169
  #
167
- # However this could also be a class:
170
+ # You can also provide a class instance:
168
171
  #
169
172
  # class Heartbeat
170
- # def self.call(env)
171
- # [200, { "Content-Type" => "text/plain" }, ["OK"]]
173
+ # def call(env)
174
+ # [200, { "content-type" => "text/plain" }, ["OK"]]
172
175
  # end
173
176
  # end
174
177
  #
175
- # run Heartbeat
176
- def run(app)
177
- @run = app
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
- # Rack::Builder.app do
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
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'constants'
4
+
3
5
  module Rack
4
6
  # Rack::Cascade tries a request on several apps, and returns the
5
7
  # first response that is not 404 or 405 (or in a list of configured
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 Content-Length header.
10
+ # when the response does not include a content-length header.
7
11
  #
8
- # This supports the Trailer response header to allow the use of trailing
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, { 'Trailer' => 'Expires'}, ["Hello", "World"]]
16
+ # [200, { 'trailer' => 'expires'}, ["Hello", "World"]]
13
17
  # # error raised
14
18
  #
15
19
  # body = ["Hello", "World"]
16
20
  # def body.trailers
17
- # { 'Expires' => Time.now.to_s }
21
+ # { 'expires' => Time.now.to_s }
18
22
  # end
19
- # [200, { 'Trailer' => 'Expires'}, body]
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 Content-Length or Transfer-Encoding headers,
96
- # modify the response to use chunked Transfer-Encoding.
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['Trailer']
108
- body = TrailerBody.new(body)
110
+ if headers['trailer']
111
+ response[2] = TrailerBody.new(body)
109
112
  else
110
- body = Body.new(body)
113
+ response[2] = Body.new(body)
111
114
  end
112
115
  end
113
116
 
114
- [status, headers, body]
117
+ response
115
118
  end
116
119
  end
117
120
  end