rack 2.2.8 → 3.0.9.1

Sign up to get free protection for your applications and to get access to all the features.
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 +9 -4
  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 +208 -178
  60. data/lib/rack/version.rb +9 -4
  61. data/lib/rack.rb +6 -76
  62. metadata +14 -34
  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