rack 2.2.8.1 → 3.1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +301 -81
  3. data/CONTRIBUTING.md +63 -55
  4. data/MIT-LICENSE +1 -1
  5. data/README.md +328 -0
  6. data/SPEC.rdoc +204 -131
  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 +838 -644
  27. data/lib/rack/lock.rb +2 -5
  28. data/lib/rack/logger.rb +3 -0
  29. data/lib/rack/method_override.rb +5 -1
  30. data/lib/rack/mime.rb +14 -5
  31. data/lib/rack/mock.rb +1 -271
  32. data/lib/rack/mock_request.rb +171 -0
  33. data/lib/rack/mock_response.rb +124 -0
  34. data/lib/rack/multipart/generator.rb +7 -5
  35. data/lib/rack/multipart/parser.rb +215 -90
  36. data/lib/rack/multipart/uploaded_file.rb +4 -0
  37. data/lib/rack/multipart.rb +53 -40
  38. data/lib/rack/null_logger.rb +9 -0
  39. data/lib/rack/query_parser.rb +81 -102
  40. data/lib/rack/recursive.rb +2 -0
  41. data/lib/rack/reloader.rb +0 -2
  42. data/lib/rack/request.rb +250 -123
  43. data/lib/rack/response.rb +146 -66
  44. data/lib/rack/rewindable_input.rb +24 -5
  45. data/lib/rack/runtime.rb +7 -6
  46. data/lib/rack/sendfile.rb +30 -25
  47. data/lib/rack/show_exceptions.rb +21 -4
  48. data/lib/rack/show_status.rb +17 -7
  49. data/lib/rack/static.rb +8 -8
  50. data/lib/rack/tempfile_reaper.rb +15 -4
  51. data/lib/rack/urlmap.rb +3 -1
  52. data/lib/rack/utils.rb +232 -233
  53. data/lib/rack/version.rb +1 -9
  54. data/lib/rack.rb +13 -89
  55. metadata +15 -41
  56. data/README.rdoc +0 -320
  57. data/Rakefile +0 -130
  58. data/bin/rackup +0 -5
  59. data/contrib/rack.png +0 -0
  60. data/contrib/rack.svg +0 -150
  61. data/contrib/rack_logo.svg +0 -164
  62. data/contrib/rdoc.css +0 -412
  63. data/example/lobster.ru +0 -6
  64. data/example/protectedlobster.rb +0 -16
  65. data/example/protectedlobster.ru +0 -10
  66. data/lib/rack/auth/digest/md5.rb +0 -131
  67. data/lib/rack/auth/digest/nonce.rb +0 -54
  68. data/lib/rack/auth/digest/params.rb +0 -54
  69. data/lib/rack/auth/digest/request.rb +0 -43
  70. data/lib/rack/chunked.rb +0 -117
  71. data/lib/rack/core_ext/regexp.rb +0 -14
  72. data/lib/rack/file.rb +0 -7
  73. data/lib/rack/handler/cgi.rb +0 -59
  74. data/lib/rack/handler/fastcgi.rb +0 -100
  75. data/lib/rack/handler/lsws.rb +0 -61
  76. data/lib/rack/handler/scgi.rb +0 -71
  77. data/lib/rack/handler/thin.rb +0 -36
  78. data/lib/rack/handler/webrick.rb +0 -129
  79. data/lib/rack/handler.rb +0 -104
  80. data/lib/rack/lobster.rb +0 -70
  81. data/lib/rack/server.rb +0 -466
  82. data/lib/rack/session/abstract/id.rb +0 -523
  83. data/lib/rack/session/cookie.rb +0 -204
  84. data/lib/rack/session/memcache.rb +0 -10
  85. data/lib/rack/session/pool.rb +0 -85
  86. 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