rack 3.0.5 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of rack might be problematic. Click here for more details.

Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +383 -1
  3. data/CONTRIBUTING.md +11 -9
  4. data/README.md +95 -28
  5. data/SPEC.rdoc +206 -288
  6. data/lib/rack/auth/abstract/request.rb +2 -0
  7. data/lib/rack/auth/basic.rb +1 -2
  8. data/lib/rack/bad_request.rb +8 -0
  9. data/lib/rack/body_proxy.rb +18 -2
  10. data/lib/rack/builder.rb +29 -10
  11. data/lib/rack/cascade.rb +0 -3
  12. data/lib/rack/common_logger.rb +3 -2
  13. data/lib/rack/conditional_get.rb +4 -3
  14. data/lib/rack/constants.rb +3 -1
  15. data/lib/rack/etag.rb +3 -0
  16. data/lib/rack/head.rb +2 -3
  17. data/lib/rack/headers.rb +86 -2
  18. data/lib/rack/lint.rb +482 -425
  19. data/lib/rack/media_type.rb +18 -9
  20. data/lib/rack/mime.rb +6 -5
  21. data/lib/rack/mock_request.rb +10 -15
  22. data/lib/rack/mock_response.rb +39 -18
  23. data/lib/rack/multipart/parser.rb +169 -73
  24. data/lib/rack/multipart/uploaded_file.rb +42 -5
  25. data/lib/rack/multipart.rb +9 -1
  26. data/lib/rack/query_parser.rb +86 -97
  27. data/lib/rack/request.rb +67 -100
  28. data/lib/rack/response.rb +29 -19
  29. data/lib/rack/rewindable_input.rb +4 -1
  30. data/lib/rack/sendfile.rb +2 -2
  31. data/lib/rack/show_exceptions.rb +10 -4
  32. data/lib/rack/show_status.rb +0 -2
  33. data/lib/rack/static.rb +2 -1
  34. data/lib/rack/utils.rb +78 -110
  35. data/lib/rack/version.rb +3 -20
  36. data/lib/rack.rb +11 -17
  37. metadata +6 -15
  38. data/lib/rack/auth/digest/md5.rb +0 -1
  39. data/lib/rack/auth/digest/nonce.rb +0 -1
  40. data/lib/rack/auth/digest/params.rb +0 -1
  41. data/lib/rack/auth/digest/request.rb +0 -1
  42. data/lib/rack/auth/digest.rb +0 -256
  43. data/lib/rack/chunked.rb +0 -120
  44. data/lib/rack/file.rb +0 -9
  45. data/lib/rack/logger.rb +0 -22
data/lib/rack/builder.rb CHANGED
@@ -2,6 +2,9 @@
2
2
 
3
3
  require_relative 'urlmap'
4
4
 
5
+ module Rack; end
6
+ Rack::BUILDER_TOPLEVEL_BINDING = ->(builder){builder.instance_eval{binding}}
7
+
5
8
  module Rack
6
9
  # Rack::Builder provides a domain-specific language (DSL) to construct Rack
7
10
  # applications. It is primarily used to parse +config.ru+ files which
@@ -53,15 +56,15 @@ module Rack
53
56
  # Rack::Builder.parse_file('app.rb')
54
57
  # # requires app.rb, which can be anywhere in Ruby's
55
58
  # # load path. After requiring, assumes App constant
56
- # # contains Rack application
59
+ # # is a Rack application
57
60
  #
58
61
  # Rack::Builder.parse_file('./my_app.rb')
59
62
  # # requires ./my_app.rb, which should be in the
60
63
  # # process's current directory. After requiring,
61
- # # assumes MyApp constant contains Rack application
62
- def self.parse_file(path)
64
+ # # assumes MyApp constant is a Rack application
65
+ def self.parse_file(path, **options)
63
66
  if path.end_with?('.ru')
64
- return self.load_file(path)
67
+ return self.load_file(path, **options)
65
68
  else
66
69
  require path
67
70
  return Object.const_get(::File.basename(path, '.rb').split('_').map(&:capitalize).join(''))
@@ -81,7 +84,7 @@ module Rack
81
84
  # use Rack::ContentLength
82
85
  # require './app.rb'
83
86
  # run App
84
- def self.load_file(path)
87
+ def self.load_file(path, **options)
85
88
  config = ::File.read(path)
86
89
  config.slice!(/\A#{UTF_8_BOM}/) if config.encoding == Encoding::UTF_8
87
90
 
@@ -91,16 +94,18 @@ module Rack
91
94
 
92
95
  config.sub!(/^__END__\n.*\Z/m, '')
93
96
 
94
- return new_from_string(config, path)
97
+ return new_from_string(config, path, **options)
95
98
  end
96
99
 
97
100
  # Evaluate the given +builder_script+ string in the context of
98
101
  # a Rack::Builder block, returning a Rack application.
99
- 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
+
100
105
  # We want to build a variant of TOPLEVEL_BINDING with self as a Rack::Builder instance.
101
106
  # We cannot use instance_eval(String) as that would resolve constants differently.
102
- binding, builder = TOPLEVEL_BINDING.eval('Rack::Builder.new.instance_eval { [binding, self] }')
103
- eval builder_script, binding, file
107
+ binding = BUILDER_TOPLEVEL_BINDING.call(builder)
108
+ eval(builder_script, binding, path)
104
109
 
105
110
  return builder.to_app
106
111
  end
@@ -108,16 +113,24 @@ module Rack
108
113
  # Initialize a new Rack::Builder instance. +default_app+ specifies the
109
114
  # default application if +run+ is not called later. If a block
110
115
  # is given, it is evaluated in the context of the instance.
111
- def initialize(default_app = nil, &block)
116
+ def initialize(default_app = nil, **options, &block)
112
117
  @use = []
113
118
  @map = nil
114
119
  @run = default_app
115
120
  @warmup = nil
116
121
  @freeze_app = false
122
+ @options = options
117
123
 
118
124
  instance_eval(&block) if block_given?
119
125
  end
120
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
+
121
134
  # Create a new Rack::Builder instance and return the Rack application
122
135
  # generated from it.
123
136
  def self.app(default_app = nil, &block)
@@ -149,6 +162,8 @@ module Rack
149
162
  @use << proc { |app| generate_map(app, mapping) }
150
163
  end
151
164
  @use << proc { |app| middleware.new(app, *args, &block) }
165
+
166
+ nil
152
167
  end
153
168
  # :nocov:
154
169
  ruby2_keywords(:use) if respond_to?(:ruby2_keywords, true)
@@ -181,6 +196,8 @@ module Rack
181
196
  raise ArgumentError, "Both app and block given!" if app && block_given?
182
197
 
183
198
  @run = app || block
199
+
200
+ nil
184
201
  end
185
202
 
186
203
  # Takes a lambda or block that is used to warm-up the application. This block is called
@@ -239,6 +256,8 @@ module Rack
239
256
  def map(path, &block)
240
257
  @map ||= {}
241
258
  @map[path] = block
259
+
260
+ nil
242
261
  end
243
262
 
244
263
  # Freeze the app (set using run) and all middleware instances when building the application
data/lib/rack/cascade.rb CHANGED
@@ -9,9 +9,6 @@ module Rack
9
9
  # status codes, return the last response.
10
10
 
11
11
  class Cascade
12
- # deprecated, no longer used
13
- NotFound = [404, { CONTENT_TYPE => "text/plain" }, []]
14
-
15
12
  # An array of applications to try in order.
16
13
  attr_reader :apps
17
14
 
@@ -20,7 +20,7 @@ module Rack
20
20
  # The actual format is slightly different than the above due to the
21
21
  # separation of SCRIPT_NAME and PATH_INFO, and because the elapsed
22
22
  # time in seconds is included at the end.
23
- FORMAT = %{%s - %s [%s] "%s %s%s%s %s" %d %s %0.4f\n}
23
+ FORMAT = %{%s - %s [%s] "%s %s%s%s %s" %d %s %0.4f }
24
24
 
25
25
  # +logger+ can be any object that supports the +write+ or +<<+ methods,
26
26
  # which includes the standard library Logger. These methods are called
@@ -66,7 +66,8 @@ module Rack
66
66
  length,
67
67
  Utils.clock_time - began_at)
68
68
 
69
- msg.gsub!(/[^[:print:]\n]/) { |c| sprintf("\\x%x", c.ord) }
69
+ msg.gsub!(/[^[:print:]]/) { |c| sprintf("\\x%x", c.ord) }
70
+ msg[-1] = "\n"
70
71
 
71
72
  logger = @logger || request.get_header(RACK_ERRORS)
72
73
  # Standard library logger doesn't support write but it supports << which actually
@@ -34,9 +34,10 @@ module Rack
34
34
  response[0] = 304
35
35
  headers.delete(CONTENT_TYPE)
36
36
  headers.delete(CONTENT_LENGTH)
37
- response[2] = Rack::BodyProxy.new([]) do
38
- body.close if body.respond_to?(:close)
39
- end
37
+
38
+ # We are done with the body:
39
+ body.close if body.respond_to?(:close)
40
+ response[2] = []
40
41
  end
41
42
  response
42
43
  else
@@ -32,6 +32,7 @@ module Rack
32
32
  DELETE = 'DELETE'
33
33
  HEAD = 'HEAD'
34
34
  OPTIONS = 'OPTIONS'
35
+ CONNECT = 'CONNECT'
35
36
  LINK = 'LINK'
36
37
  UNLINK = 'UNLINK'
37
38
  TRACE = 'TRACE'
@@ -39,6 +40,7 @@ module Rack
39
40
  # Rack environment variables
40
41
  RACK_VERSION = 'rack.version'
41
42
  RACK_TEMPFILES = 'rack.tempfiles'
43
+ RACK_EARLY_HINTS = 'rack.early_hints'
42
44
  RACK_ERRORS = 'rack.errors'
43
45
  RACK_LOGGER = 'rack.logger'
44
46
  RACK_INPUT = 'rack.input'
@@ -52,6 +54,7 @@ module Rack
52
54
  RACK_MULTIPART_BUFFER_SIZE = 'rack.multipart.buffer_size'
53
55
  RACK_MULTIPART_TEMPFILE_FACTORY = 'rack.multipart.tempfile_factory'
54
56
  RACK_RESPONSE_FINISHED = 'rack.response_finished'
57
+ RACK_PROTOCOL = 'rack.protocol'
55
58
  RACK_REQUEST_FORM_INPUT = 'rack.request.form_input'
56
59
  RACK_REQUEST_FORM_HASH = 'rack.request.form_hash'
57
60
  RACK_REQUEST_FORM_PAIRS = 'rack.request.form_pairs'
@@ -60,7 +63,6 @@ module Rack
60
63
  RACK_REQUEST_COOKIE_HASH = 'rack.request.cookie_hash'
61
64
  RACK_REQUEST_COOKIE_STRING = 'rack.request.cookie_string'
62
65
  RACK_REQUEST_QUERY_HASH = 'rack.request.query_hash'
63
- RACK_REQUEST_QUERY_PAIRS = 'rack.request.query_pairs'
64
66
  RACK_REQUEST_QUERY_STRING = 'rack.request.query_string'
65
67
  RACK_METHODOVERRIDE_ORIGINAL_METHOD = 'rack.methodoverride.original_method'
66
68
  end
data/lib/rack/etag.rb CHANGED
@@ -32,6 +32,9 @@ module Rack
32
32
  body = body.to_ary
33
33
  digest = digest_body(body)
34
34
  headers[ETAG_STRING] = %(W/"#{digest}") if digest
35
+
36
+ # Body was modified, so we need to re-assign it:
37
+ response[2] = body
35
38
  end
36
39
 
37
40
  unless headers[CACHE_CONTROL]
data/lib/rack/head.rb CHANGED
@@ -15,9 +15,8 @@ module Rack
15
15
  _, _, body = response = @app.call(env)
16
16
 
17
17
  if env[REQUEST_METHOD] == HEAD
18
- response[2] = Rack::BodyProxy.new([]) do
19
- body.close if body.respond_to? :close
20
- end
18
+ body.close if body.respond_to?(:close)
19
+ response[2] = []
21
20
  end
22
21
 
23
22
  response
data/lib/rack/headers.rb CHANGED
@@ -1,9 +1,93 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
4
  # Rack::Headers is a Hash subclass that downcases all keys. It's designed
3
5
  # to be used by rack applications that don't implement the Rack 3 SPEC
4
6
  # (by using non-lowercase response header keys), automatically handling
5
7
  # the downcasing of keys.
6
8
  class Headers < Hash
9
+ KNOWN_HEADERS = {}
10
+ %w(
11
+ Accept-CH
12
+ Accept-Patch
13
+ Accept-Ranges
14
+ Access-Control-Allow-Credentials
15
+ Access-Control-Allow-Headers
16
+ Access-Control-Allow-Methods
17
+ Access-Control-Allow-Origin
18
+ Access-Control-Expose-Headers
19
+ Access-Control-Max-Age
20
+ Age
21
+ Allow
22
+ Alt-Svc
23
+ Cache-Control
24
+ Connection
25
+ Content-Disposition
26
+ Content-Encoding
27
+ Content-Language
28
+ Content-Length
29
+ Content-Location
30
+ Content-MD5
31
+ Content-Range
32
+ Content-Security-Policy
33
+ Content-Security-Policy-Report-Only
34
+ Content-Type
35
+ Date
36
+ Delta-Base
37
+ ETag
38
+ Expect-CT
39
+ Expires
40
+ Feature-Policy
41
+ IM
42
+ Last-Modified
43
+ Link
44
+ Location
45
+ NEL
46
+ P3P
47
+ Permissions-Policy
48
+ Pragma
49
+ Preference-Applied
50
+ Proxy-Authenticate
51
+ Public-Key-Pins
52
+ Referrer-Policy
53
+ Refresh
54
+ Report-To
55
+ Retry-After
56
+ Server
57
+ Set-Cookie
58
+ Status
59
+ Strict-Transport-Security
60
+ Timing-Allow-Origin
61
+ Tk
62
+ Trailer
63
+ Transfer-Encoding
64
+ Upgrade
65
+ Vary
66
+ Via
67
+ WWW-Authenticate
68
+ Warning
69
+ X-Cascade
70
+ X-Content-Duration
71
+ X-Content-Security-Policy
72
+ X-Content-Type-Options
73
+ X-Correlation-ID
74
+ X-Correlation-Id
75
+ X-Download-Options
76
+ X-Frame-Options
77
+ X-Permitted-Cross-Domain-Policies
78
+ X-Powered-By
79
+ X-Redirect-By
80
+ X-Request-ID
81
+ X-Request-Id
82
+ X-Runtime
83
+ X-UA-Compatible
84
+ X-WebKit-CS
85
+ X-XSS-Protection
86
+ ).each do |str|
87
+ downcased = str.downcase.freeze
88
+ KNOWN_HEADERS[str] = KNOWN_HEADERS[downcased] = downcased
89
+ end
90
+
7
91
  def self.[](*items)
8
92
  if items.length % 2 != 0
9
93
  if items.length == 1 && items.first.is_a?(Hash)
@@ -28,7 +112,7 @@ module Rack
28
112
  end
29
113
 
30
114
  def []=(key, value)
31
- super(key.downcase.freeze, value)
115
+ super(KNOWN_HEADERS[key] || key.downcase.freeze, value)
32
116
  end
33
117
  alias store []=
34
118
 
@@ -148,7 +232,7 @@ module Rack
148
232
  private
149
233
 
150
234
  def downcase_key(key)
151
- key.is_a?(String) ? key.downcase : key
235
+ key.is_a?(String) ? KNOWN_HEADERS[key] || key.downcase : key
152
236
  end
153
237
  end
154
238
  end