rack 2.2.7 → 3.1.3

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 +291 -78
  3. data/CONTRIBUTING.md +63 -55
  4. data/MIT-LICENSE +1 -1
  5. data/README.md +328 -0
  6. data/SPEC.rdoc +213 -136
  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 +1 -4
  10. data/lib/rack/bad_request.rb +8 -0
  11. data/lib/rack/body_proxy.rb +21 -3
  12. data/lib/rack/builder.rb +102 -69
  13. data/lib/rack/cascade.rb +2 -3
  14. data/lib/rack/common_logger.rb +23 -18
  15. data/lib/rack/conditional_get.rb +18 -15
  16. data/lib/rack/constants.rb +67 -0
  17. data/lib/rack/content_length.rb +12 -16
  18. data/lib/rack/content_type.rb +8 -5
  19. data/lib/rack/deflater.rb +40 -26
  20. data/lib/rack/directory.rb +9 -3
  21. data/lib/rack/etag.rb +14 -23
  22. data/lib/rack/events.rb +4 -0
  23. data/lib/rack/files.rb +15 -17
  24. data/lib/rack/head.rb +9 -8
  25. data/lib/rack/headers.rb +238 -0
  26. data/lib/rack/lint.rb +864 -681
  27. data/lib/rack/lock.rb +2 -5
  28. data/lib/rack/logger.rb +3 -0
  29. data/lib/rack/media_type.rb +9 -4
  30. data/lib/rack/method_override.rb +5 -1
  31. data/lib/rack/mime.rb +14 -5
  32. data/lib/rack/mock.rb +1 -271
  33. data/lib/rack/mock_request.rb +171 -0
  34. data/lib/rack/mock_response.rb +124 -0
  35. data/lib/rack/multipart/generator.rb +7 -5
  36. data/lib/rack/multipart/parser.rb +218 -91
  37. data/lib/rack/multipart/uploaded_file.rb +4 -0
  38. data/lib/rack/multipart.rb +53 -40
  39. data/lib/rack/null_logger.rb +9 -0
  40. data/lib/rack/query_parser.rb +81 -102
  41. data/lib/rack/recursive.rb +2 -0
  42. data/lib/rack/reloader.rb +0 -2
  43. data/lib/rack/request.rb +248 -123
  44. data/lib/rack/response.rb +146 -66
  45. data/lib/rack/rewindable_input.rb +24 -5
  46. data/lib/rack/runtime.rb +7 -6
  47. data/lib/rack/sendfile.rb +30 -25
  48. data/lib/rack/show_exceptions.rb +21 -4
  49. data/lib/rack/show_status.rb +17 -7
  50. data/lib/rack/static.rb +8 -8
  51. data/lib/rack/tempfile_reaper.rb +15 -4
  52. data/lib/rack/urlmap.rb +3 -1
  53. data/lib/rack/utils.rb +237 -235
  54. data/lib/rack/version.rb +1 -9
  55. data/lib/rack.rb +13 -89
  56. metadata +15 -41
  57. data/README.rdoc +0 -320
  58. data/Rakefile +0 -130
  59. data/bin/rackup +0 -5
  60. data/contrib/rack.png +0 -0
  61. data/contrib/rack.svg +0 -150
  62. data/contrib/rack_logo.svg +0 -164
  63. data/contrib/rdoc.css +0 -412
  64. data/example/lobster.ru +0 -6
  65. data/example/protectedlobster.rb +0 -16
  66. data/example/protectedlobster.ru +0 -10
  67. data/lib/rack/auth/digest/md5.rb +0 -131
  68. data/lib/rack/auth/digest/nonce.rb +0 -54
  69. data/lib/rack/auth/digest/params.rb +0 -54
  70. data/lib/rack/auth/digest/request.rb +0 -43
  71. data/lib/rack/chunked.rb +0 -117
  72. data/lib/rack/core_ext/regexp.rb +0 -14
  73. data/lib/rack/file.rb +0 -7
  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 -203
  85. data/lib/rack/session/memcache.rb +0 -10
  86. data/lib/rack/session/pool.rb +0 -85
  87. data/rack.gemspec +0 -46
data/lib/rack/builder.rb CHANGED
@@ -1,35 +1,38 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'urlmap'
4
+
5
+ module Rack; end
6
+ Rack::BUILDER_TOPLEVEL_BINDING = ->(builder){builder.instance_eval{binding}}
7
+
3
8
  module Rack
4
- # Rack::Builder implements a small DSL to iteratively construct Rack
5
- # applications.
9
+ # Rack::Builder provides a domain-specific language (DSL) to construct Rack
10
+ # applications. It is primarily used to parse +config.ru+ files which
11
+ # instantiate several middleware and a final application which are hosted
12
+ # by a Rack-compatible web server.
6
13
  #
7
14
  # Example:
8
15
  #
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
16
+ # app = Rack::Builder.new do
17
+ # use Rack::CommonLogger
18
+ # map "/ok" do
19
+ # run lambda { |env| [200, {'content-type' => 'text/plain'}, ['OK']] }
20
+ # end
21
+ # end
18
22
  #
19
- # run app
23
+ # run app
20
24
  #
21
25
  # Or
22
26
  #
23
- # app = Rack::Builder.app do
24
- # use Rack::CommonLogger
25
- # run lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['OK']] }
26
- # end
27
+ # app = Rack::Builder.app do
28
+ # use Rack::CommonLogger
29
+ # run lambda { |env| [200, {'content-type' => 'text/plain'}, ['OK']] }
30
+ # end
27
31
  #
28
- # run app
32
+ # run app
29
33
  #
30
34
  # +use+ adds middleware to the stack, +run+ dispatches to an application.
31
35
  # You can use +map+ to construct a Rack::URLMap in a convenient way.
32
-
33
36
  class Builder
34
37
 
35
38
  # https://stackoverflow.com/questions/2223882/whats-the-difference-between-utf-8-and-utf-8-without-bom
@@ -39,13 +42,11 @@ module Rack
39
42
  #
40
43
  # If the config file ends in +.ru+, it is treated as a
41
44
  # rackup file and the contents will be treated as if
42
- # specified inside a Rack::Builder block, using the given
43
- # options.
45
+ # specified inside a Rack::Builder block.
44
46
  #
45
47
  # If the config file does not end in +.ru+, it is
46
48
  # required and Rack will use the basename of the file
47
49
  # to guess which constant will be the Rack application to run.
48
- # The options given will be ignored in this case.
49
50
  #
50
51
  # Examples:
51
52
  #
@@ -55,29 +56,24 @@ module Rack
55
56
  # Rack::Builder.parse_file('app.rb')
56
57
  # # requires app.rb, which can be anywhere in Ruby's
57
58
  # # load path. After requiring, assumes App constant
58
- # # contains Rack application
59
+ # # is a Rack application
59
60
  #
60
61
  # Rack::Builder.parse_file('./my_app.rb')
61
62
  # # requires ./my_app.rb, which should be in the
62
63
  # # process's current directory. After requiring,
63
- # # 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)
64
+ # # assumes MyApp constant is a Rack application
65
+ def self.parse_file(path, **options)
66
+ if path.end_with?('.ru')
67
+ return self.load_file(path, **options)
67
68
  else
68
- require config
69
- app = Object.const_get(::File.basename(config, '.rb').split('_').map(&:capitalize).join(''))
70
- return app, {}
69
+ require path
70
+ return Object.const_get(::File.basename(path, '.rb').split('_').map(&:capitalize).join(''))
71
71
  end
72
72
  end
73
73
 
74
74
  # Load the given file as a rackup file, treating the
75
75
  # contents as if specified inside a Rack::Builder block.
76
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
77
  # Ignores content in the file after +__END__+, so that
82
78
  # use of +__END__+ will not result in a syntax error.
83
79
  #
@@ -85,46 +81,56 @@ module Rack
85
81
  #
86
82
  # $ cat config.ru
87
83
  #
88
- # #\ -p 9393
89
- #
90
84
  # use Rack::ContentLength
91
85
  # require './app.rb'
92
86
  # 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
87
+ def self.load_file(path, **options)
88
+ config = ::File.read(path)
89
+ config.slice!(/\A#{UTF_8_BOM}/) if config.encoding == Encoding::UTF_8
98
90
 
99
- if cfgfile[/^#\\(.*)/] && opts
100
- warn "Parsing options from the first comment line is deprecated!"
101
- options = opts.parse! $1.split(/\s+/)
91
+ if config[/^#\\(.*)/]
92
+ fail "Parsing options from the first comment line is no longer supported: #{path}"
102
93
  end
103
94
 
104
- cfgfile.sub!(/^__END__\n.*\Z/m, '')
105
- app = new_from_string cfgfile, path
95
+ config.sub!(/^__END__\n.*\Z/m, '')
106
96
 
107
- return app, options
97
+ return new_from_string(config, path, **options)
108
98
  end
109
99
 
110
100
  # Evaluate the given +builder_script+ string in the context of
111
101
  # a Rack::Builder block, returning a Rack application.
112
- def self.new_from_string(builder_script, file = "(rackup)")
102
+ def self.new_from_string(builder_script, path = "(rackup)", **options)
103
+ builder = self.new(**options)
104
+
113
105
  # We want to build a variant of TOPLEVEL_BINDING with self as a Rack::Builder instance.
114
106
  # 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
107
+ binding = BUILDER_TOPLEVEL_BINDING.call(builder)
108
+ eval(builder_script, binding, path)
109
+
110
+ return builder.to_app
118
111
  end
119
112
 
120
113
  # Initialize a new Rack::Builder instance. +default_app+ specifies the
121
114
  # default application if +run+ is not called later. If a block
122
- # is given, it is evaluted in the context of the instance.
123
- def initialize(default_app = nil, &block)
124
- @use, @map, @run, @warmup, @freeze_app = [], nil, default_app, nil, false
115
+ # is given, it is evaluated in the context of the instance.
116
+ def initialize(default_app = nil, **options, &block)
117
+ @use = []
118
+ @map = nil
119
+ @run = default_app
120
+ @warmup = nil
121
+ @freeze_app = false
122
+ @options = options
123
+
125
124
  instance_eval(&block) if block_given?
126
125
  end
127
126
 
127
+ # Any options provided to the Rack::Builder instance at initialization.
128
+ # These options can be server-specific. Some general options are:
129
+ #
130
+ # * +:isolation+: One of +process+, +thread+ or +fiber+. The execution
131
+ # isolation model to use.
132
+ attr :options
133
+
128
134
  # Create a new Rack::Builder instance and return the Rack application
129
135
  # generated from it.
130
136
  def self.app(default_app = nil, &block)
@@ -145,7 +151,7 @@ module Rack
145
151
  # end
146
152
  #
147
153
  # use Middleware
148
- # run lambda { |env| [200, { "Content-Type" => "text/plain" }, ["OK"]] }
154
+ # run lambda { |env| [200, { "content-type" => "text/plain" }, ["OK"]] }
149
155
  #
150
156
  # All requests through to this application will first be processed by the middleware class.
151
157
  # The +call+ method in this example sets an additional environment key which then can be
@@ -157,24 +163,37 @@ module Rack
157
163
  end
158
164
  @use << proc { |app| middleware.new(app, *args, &block) }
159
165
  end
166
+ # :nocov:
160
167
  ruby2_keywords(:use) if respond_to?(:ruby2_keywords, true)
168
+ # :nocov:
161
169
 
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:
170
+ # Takes a block or argument that is an object that responds to #call and
171
+ # returns a Rack response.
172
+ #
173
+ # You can use a block:
174
+ #
175
+ # run do |env|
176
+ # [200, { "content-type" => "text/plain" }, ["Hello World!"]]
177
+ # end
164
178
  #
165
- # run lambda { |env| [200, { "Content-Type" => "text/plain" }, ["OK"]] }
179
+ # You can also provide a lambda:
166
180
  #
167
- # However this could also be a class:
181
+ # run lambda { |env| [200, { "content-type" => "text/plain" }, ["OK"]] }
182
+ #
183
+ # You can also provide a class instance:
168
184
  #
169
185
  # class Heartbeat
170
- # def self.call(env)
171
- # [200, { "Content-Type" => "text/plain" }, ["OK"]]
186
+ # def call(env)
187
+ # [200, { "content-type" => "text/plain" }, ["OK"]]
172
188
  # end
173
189
  # end
174
190
  #
175
- # run Heartbeat
176
- def run(app)
177
- @run = app
191
+ # run Heartbeat.new
192
+ #
193
+ def run(app = nil, &block)
194
+ raise ArgumentError, "Both app and block given!" if app && block_given?
195
+
196
+ @run = app || block
178
197
  end
179
198
 
180
199
  # Takes a lambda or block that is used to warm-up the application. This block is called
@@ -195,21 +214,35 @@ module Rack
195
214
  # the Rack application specified by run inside the block. Other requests will be sent to the
196
215
  # default application specified by run outside the block.
197
216
  #
198
- # Rack::Builder.app do
217
+ # class App
218
+ # def call(env)
219
+ # [200, {'content-type' => 'text/plain'}, ["Hello World"]]
220
+ # end
221
+ # end
222
+ #
223
+ # class Heartbeat
224
+ # def call(env)
225
+ # [200, { "content-type" => "text/plain" }, ["OK"]]
226
+ # end
227
+ # end
228
+ #
229
+ # app = Rack::Builder.app do
199
230
  # map '/heartbeat' do
200
- # run Heartbeat
231
+ # run Heartbeat.new
201
232
  # end
202
- # run App
233
+ # run App.new
203
234
  # end
204
235
  #
236
+ # run app
237
+ #
205
238
  # The +use+ method can also be used inside the block to specify middleware to run under a specific path:
206
239
  #
207
- # Rack::Builder.app do
240
+ # app = Rack::Builder.app do
208
241
  # map '/heartbeat' do
209
242
  # use Middleware
210
- # run Heartbeat
243
+ # run Heartbeat.new
211
244
  # end
212
- # run App
245
+ # run App.new
213
246
  # end
214
247
  #
215
248
  # 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
@@ -7,9 +9,6 @@ module Rack
7
9
  # status codes, return the last response.
8
10
 
9
11
  class Cascade
10
- # deprecated, no longer used
11
- NotFound = [404, { CONTENT_TYPE => "text/plain" }, []]
12
-
13
12
  # An array of applications to try in order.
14
13
  attr_reader :apps
15
14
 
@@ -1,5 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'constants'
4
+ require_relative 'utils'
5
+ require_relative 'body_proxy'
6
+ require_relative 'request'
7
+
3
8
  module Rack
4
9
  # Rack::CommonLogger forwards every request to the given +app+, and
5
10
  # logs a line in the
@@ -35,35 +40,35 @@ module Rack
35
40
  # cause the request not to be logged.
36
41
  def call(env)
37
42
  began_at = Utils.clock_time
38
- status, headers, body = @app.call(env)
39
- headers = Utils::HeaderHash[headers]
40
- body = BodyProxy.new(body) { log(env, status, headers, began_at) }
41
- [status, headers, body]
43
+ status, headers, body = response = @app.call(env)
44
+
45
+ response[2] = BodyProxy.new(body) { log(env, status, headers, began_at) }
46
+ response
42
47
  end
43
48
 
44
49
  private
45
50
 
46
51
  # Log the request to the configured logger.
47
- def log(env, status, header, began_at)
48
- length = extract_content_length(header)
52
+ def log(env, status, response_headers, began_at)
53
+ request = Rack::Request.new(env)
54
+ length = extract_content_length(response_headers)
49
55
 
50
- msg = FORMAT % [
51
- env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-",
52
- env["REMOTE_USER"] || "-",
56
+ msg = sprintf(FORMAT,
57
+ request.ip || "-",
58
+ request.get_header("REMOTE_USER") || "-",
53
59
  Time.now.strftime("%d/%b/%Y:%H:%M:%S %z"),
54
- env[REQUEST_METHOD],
55
- env[SCRIPT_NAME],
56
- env[PATH_INFO],
57
- env[QUERY_STRING].empty? ? "" : "?#{env[QUERY_STRING]}",
58
- env[SERVER_PROTOCOL],
60
+ request.request_method,
61
+ request.script_name,
62
+ request.path_info,
63
+ request.query_string.empty? ? "" : "?#{request.query_string}",
64
+ request.get_header(SERVER_PROTOCOL),
59
65
  status.to_s[0..3],
60
66
  length,
61
- Utils.clock_time - began_at ]
62
-
63
- msg.gsub!(/[^[:print:]\n]/) { |c| "\\x#{c.ord}" }
67
+ Utils.clock_time - began_at)
64
68
 
65
- logger = @logger || env[RACK_ERRORS]
69
+ msg.gsub!(/[^[:print:]\n]/) { |c| sprintf("\\x%x", c.ord) }
66
70
 
71
+ logger = @logger || request.get_header(RACK_ERRORS)
67
72
  # Standard library logger doesn't support write but it supports << which actually
68
73
  # calls to write on the log device without formatting
69
74
  if logger.respond_to?(:write)
@@ -1,10 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'constants'
4
+ require_relative 'utils'
5
+ require_relative 'body_proxy'
6
+
3
7
  module Rack
4
8
 
5
- # Middleware that enables conditional GET using If-None-Match and
6
- # If-Modified-Since. The application should set either or both of the
7
- # Last-Modified or Etag response headers according to RFC 2616. When
9
+ # Middleware that enables conditional GET using if-none-match and
10
+ # if-modified-since. The application should set either or both of the
11
+ # last-modified or etag response headers according to RFC 2616. When
8
12
  # either of the conditions is met, the response body is set to be zero
9
13
  # length and the response status is set to 304 Not Modified.
10
14
  #
@@ -24,18 +28,17 @@ module Rack
24
28
  def call(env)
25
29
  case env[REQUEST_METHOD]
26
30
  when "GET", "HEAD"
27
- status, headers, body = @app.call(env)
28
- headers = Utils::HeaderHash[headers]
31
+ status, headers, body = response = @app.call(env)
32
+
29
33
  if status == 200 && fresh?(env, headers)
30
- status = 304
34
+ response[0] = 304
31
35
  headers.delete(CONTENT_TYPE)
32
36
  headers.delete(CONTENT_LENGTH)
33
- original_body = body
34
- body = Rack::BodyProxy.new([]) do
35
- original_body.close if original_body.respond_to?(:close)
37
+ response[2] = Rack::BodyProxy.new([]) do
38
+ body.close if body.respond_to?(:close)
36
39
  end
37
40
  end
38
- [status, headers, body]
41
+ response
39
42
  else
40
43
  @app.call(env)
41
44
  end
@@ -46,7 +49,7 @@ module Rack
46
49
  # Return whether the response has not been modified since the
47
50
  # last request.
48
51
  def fresh?(env, headers)
49
- # If-None-Match has priority over If-Modified-Since per RFC 7232
52
+ # if-none-match has priority over if-modified-since per RFC 7232
50
53
  if none_match = env['HTTP_IF_NONE_MATCH']
51
54
  etag_matches?(none_match, headers)
52
55
  elsif (modified_since = env['HTTP_IF_MODIFIED_SINCE']) && (modified_since = to_rfc2822(modified_since))
@@ -54,16 +57,16 @@ module Rack
54
57
  end
55
58
  end
56
59
 
57
- # Whether the ETag response header matches the If-None-Match request header.
60
+ # Whether the etag response header matches the if-none-match request header.
58
61
  # If so, the request has not been modified.
59
62
  def etag_matches?(none_match, headers)
60
- headers['ETag'] == none_match
63
+ headers[ETAG] == none_match
61
64
  end
62
65
 
63
- # Whether the Last-Modified response header matches the If-Modified-Since
66
+ # Whether the last-modified response header matches the if-modified-since
64
67
  # request header. If so, the request has not been modified.
65
68
  def modified_since?(modified_since, headers)
66
- last_modified = to_rfc2822(headers['Last-Modified']) and
69
+ last_modified = to_rfc2822(headers['last-modified']) and
67
70
  modified_since >= last_modified
68
71
  end
69
72
 
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rack
4
+ # Request env keys
5
+ HTTP_HOST = 'HTTP_HOST'
6
+ HTTP_PORT = 'HTTP_PORT'
7
+ HTTPS = 'HTTPS'
8
+ PATH_INFO = 'PATH_INFO'
9
+ REQUEST_METHOD = 'REQUEST_METHOD'
10
+ REQUEST_PATH = 'REQUEST_PATH'
11
+ SCRIPT_NAME = 'SCRIPT_NAME'
12
+ QUERY_STRING = 'QUERY_STRING'
13
+ SERVER_PROTOCOL = 'SERVER_PROTOCOL'
14
+ SERVER_NAME = 'SERVER_NAME'
15
+ SERVER_PORT = 'SERVER_PORT'
16
+ HTTP_COOKIE = 'HTTP_COOKIE'
17
+
18
+ # Response Header Keys
19
+ CACHE_CONTROL = 'cache-control'
20
+ CONTENT_LENGTH = 'content-length'
21
+ CONTENT_TYPE = 'content-type'
22
+ ETAG = 'etag'
23
+ EXPIRES = 'expires'
24
+ SET_COOKIE = 'set-cookie'
25
+ TRANSFER_ENCODING = 'transfer-encoding'
26
+
27
+ # HTTP method verbs
28
+ GET = 'GET'
29
+ POST = 'POST'
30
+ PUT = 'PUT'
31
+ PATCH = 'PATCH'
32
+ DELETE = 'DELETE'
33
+ HEAD = 'HEAD'
34
+ OPTIONS = 'OPTIONS'
35
+ CONNECT = 'CONNECT'
36
+ LINK = 'LINK'
37
+ UNLINK = 'UNLINK'
38
+ TRACE = 'TRACE'
39
+
40
+ # Rack environment variables
41
+ RACK_VERSION = 'rack.version'
42
+ RACK_TEMPFILES = 'rack.tempfiles'
43
+ RACK_EARLY_HINTS = 'rack.early_hints'
44
+ RACK_ERRORS = 'rack.errors'
45
+ RACK_LOGGER = 'rack.logger'
46
+ RACK_INPUT = 'rack.input'
47
+ RACK_SESSION = 'rack.session'
48
+ RACK_SESSION_OPTIONS = 'rack.session.options'
49
+ RACK_SHOWSTATUS_DETAIL = 'rack.showstatus.detail'
50
+ RACK_URL_SCHEME = 'rack.url_scheme'
51
+ RACK_HIJACK = 'rack.hijack'
52
+ RACK_IS_HIJACK = 'rack.hijack?'
53
+ RACK_RECURSIVE_INCLUDE = 'rack.recursive.include'
54
+ RACK_MULTIPART_BUFFER_SIZE = 'rack.multipart.buffer_size'
55
+ RACK_MULTIPART_TEMPFILE_FACTORY = 'rack.multipart.tempfile_factory'
56
+ RACK_RESPONSE_FINISHED = 'rack.response_finished'
57
+ RACK_REQUEST_FORM_INPUT = 'rack.request.form_input'
58
+ RACK_REQUEST_FORM_HASH = 'rack.request.form_hash'
59
+ RACK_REQUEST_FORM_PAIRS = 'rack.request.form_pairs'
60
+ RACK_REQUEST_FORM_VARS = 'rack.request.form_vars'
61
+ RACK_REQUEST_FORM_ERROR = 'rack.request.form_error'
62
+ RACK_REQUEST_COOKIE_HASH = 'rack.request.cookie_hash'
63
+ RACK_REQUEST_COOKIE_STRING = 'rack.request.cookie_string'
64
+ RACK_REQUEST_QUERY_HASH = 'rack.request.query_hash'
65
+ RACK_REQUEST_QUERY_STRING = 'rack.request.query_string'
66
+ RACK_METHODOVERRIDE_ORIGINAL_METHOD = 'rack.methodoverride.original_method'
67
+ end
@@ -1,10 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'constants'
4
+ require_relative 'utils'
5
+
3
6
  module Rack
4
7
 
5
- # Sets the Content-Length header on responses that do not specify
6
- # a Content-Length or Transfer-Encoding header. Note that this
7
- # does not fix responses that have an invalid Content-Length
8
+ # Sets the content-length header on responses that do not specify
9
+ # a content-length or transfer-encoding header. Note that this
10
+ # does not fix responses that have an invalid content-length
8
11
  # header specified.
9
12
  class ContentLength
10
13
  include Rack::Utils
@@ -14,25 +17,18 @@ module Rack
14
17
  end
15
18
 
16
19
  def call(env)
17
- status, headers, body = @app.call(env)
18
- headers = HeaderHash[headers]
20
+ status, headers, body = response = @app.call(env)
19
21
 
20
22
  if !STATUS_WITH_NO_ENTITY_BODY.key?(status.to_i) &&
21
23
  !headers[CONTENT_LENGTH] &&
22
- !headers[TRANSFER_ENCODING]
23
-
24
- obody = body
25
- body, length = [], 0
26
- obody.each { |part| body << part; length += part.bytesize }
27
-
28
- body = BodyProxy.new(body) do
29
- obody.close if obody.respond_to?(:close)
30
- end
24
+ !headers[TRANSFER_ENCODING] &&
25
+ body.respond_to?(:to_ary)
31
26
 
32
- headers[CONTENT_LENGTH] = length.to_s
27
+ response[2] = body = body.to_ary
28
+ headers[CONTENT_LENGTH] = body.sum(&:bytesize).to_s
33
29
  end
34
30
 
35
- [status, headers, body]
31
+ response
36
32
  end
37
33
  end
38
34
  end
@@ -1,8 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'constants'
4
+ require_relative 'utils'
5
+
3
6
  module Rack
4
7
 
5
- # Sets the Content-Type header on responses which don't have one.
8
+ # Sets the content-type header on responses which don't have one.
6
9
  #
7
10
  # Builder Usage:
8
11
  # use Rack::ContentType, "text/plain"
@@ -13,18 +16,18 @@ module Rack
13
16
  include Rack::Utils
14
17
 
15
18
  def initialize(app, content_type = "text/html")
16
- @app, @content_type = app, content_type
19
+ @app = app
20
+ @content_type = content_type
17
21
  end
18
22
 
19
23
  def call(env)
20
- status, headers, body = @app.call(env)
21
- headers = Utils::HeaderHash[headers]
24
+ status, headers, _ = response = @app.call(env)
22
25
 
23
26
  unless STATUS_WITH_NO_ENTITY_BODY.key?(status.to_i)
24
27
  headers[CONTENT_TYPE] ||= @content_type
25
28
  end
26
29
 
27
- [status, headers, body]
30
+ response
28
31
  end
29
32
  end
30
33
  end