rack 3.0.15 → 3.2.6
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +368 -6
- data/CONTRIBUTING.md +11 -9
- data/README.md +103 -28
- data/SPEC.rdoc +206 -288
- data/lib/rack/auth/abstract/request.rb +2 -0
- data/lib/rack/auth/basic.rb +1 -2
- data/lib/rack/bad_request.rb +8 -0
- data/lib/rack/builder.rb +29 -10
- data/lib/rack/cascade.rb +0 -3
- data/lib/rack/conditional_get.rb +4 -3
- data/lib/rack/constants.rb +4 -0
- data/lib/rack/directory.rb +6 -3
- data/lib/rack/events.rb +21 -6
- data/lib/rack/files.rb +1 -1
- data/lib/rack/head.rb +2 -3
- data/lib/rack/headers.rb +86 -2
- data/lib/rack/lint.rb +482 -425
- data/lib/rack/media_type.rb +14 -10
- data/lib/rack/mime.rb +6 -5
- data/lib/rack/mock_request.rb +10 -15
- data/lib/rack/mock_response.rb +50 -20
- data/lib/rack/multipart/parser.rb +255 -76
- data/lib/rack/multipart/uploaded_file.rb +42 -5
- data/lib/rack/multipart.rb +34 -1
- data/lib/rack/query_parser.rb +86 -78
- data/lib/rack/request.rb +78 -65
- data/lib/rack/response.rb +28 -20
- data/lib/rack/rewindable_input.rb +4 -1
- data/lib/rack/sendfile.rb +51 -21
- data/lib/rack/show_exceptions.rb +10 -4
- data/lib/rack/show_status.rb +0 -2
- data/lib/rack/static.rb +7 -3
- data/lib/rack/utils.rb +175 -119
- data/lib/rack/version.rb +3 -20
- data/lib/rack.rb +1 -4
- metadata +6 -12
- data/lib/rack/auth/digest/md5.rb +0 -1
- data/lib/rack/auth/digest/nonce.rb +0 -1
- data/lib/rack/auth/digest/params.rb +0 -1
- data/lib/rack/auth/digest/request.rb +0 -1
- data/lib/rack/auth/digest.rb +0 -256
- data/lib/rack/chunked.rb +0 -120
- data/lib/rack/file.rb +0 -9
- 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
|
-
# #
|
|
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
|
|
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,
|
|
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
|
|
103
|
-
eval
|
|
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
data/lib/rack/conditional_get.rb
CHANGED
|
@@ -34,9 +34,10 @@ module Rack
|
|
|
34
34
|
response[0] = 304
|
|
35
35
|
headers.delete(CONTENT_TYPE)
|
|
36
36
|
headers.delete(CONTENT_LENGTH)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
data/lib/rack/constants.rb
CHANGED
|
@@ -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,8 +54,10 @@ 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'
|
|
60
|
+
RACK_REQUEST_FORM_PAIRS = 'rack.request.form_pairs'
|
|
57
61
|
RACK_REQUEST_FORM_VARS = 'rack.request.form_vars'
|
|
58
62
|
RACK_REQUEST_FORM_ERROR = 'rack.request.form_error'
|
|
59
63
|
RACK_REQUEST_COOKIE_HASH = 'rack.request.cookie_hash'
|
data/lib/rack/directory.rb
CHANGED
|
@@ -17,7 +17,7 @@ module Rack
|
|
|
17
17
|
# If +app+ is not specified, a Rack::Files of the same +root+ will be used.
|
|
18
18
|
|
|
19
19
|
class Directory
|
|
20
|
-
DIR_FILE = "<tr><td class='name'><a href='
|
|
20
|
+
DIR_FILE = "<tr><td class='name'><a href='./%s'>%s</a></td><td class='size'>%s</td><td class='type'>%s</td><td class='mtime'>%s</td></tr>\n"
|
|
21
21
|
DIR_PAGE_HEADER = <<-PAGE
|
|
22
22
|
<html><head>
|
|
23
23
|
<title>%s</title>
|
|
@@ -51,7 +51,7 @@ table { width:100%%; }
|
|
|
51
51
|
class DirectoryBody < Struct.new(:root, :path, :files)
|
|
52
52
|
# Yield strings for each part of the directory entry
|
|
53
53
|
def each
|
|
54
|
-
show_path = Utils.escape_html(path.sub(
|
|
54
|
+
show_path = Utils.escape_html(path.sub(/\A#{Regexp.escape(root)}/, ''))
|
|
55
55
|
yield(DIR_PAGE_HEADER % [ show_path, show_path ])
|
|
56
56
|
|
|
57
57
|
unless path.chomp('/') == root
|
|
@@ -82,6 +82,7 @@ table { width:100%%; }
|
|
|
82
82
|
# Set the root directory and application for serving files.
|
|
83
83
|
def initialize(root, app = nil)
|
|
84
84
|
@root = ::File.expand_path(root)
|
|
85
|
+
@root_with_separator = @root.end_with?(::File::SEPARATOR) ? @root : "#{@root}#{::File::SEPARATOR}"
|
|
85
86
|
@app = app || Files.new(@root)
|
|
86
87
|
@head = Head.new(method(:get))
|
|
87
88
|
end
|
|
@@ -118,7 +119,9 @@ table { width:100%%; }
|
|
|
118
119
|
# Rack response to use for requests with paths outside the root, or nil if path is inside the root.
|
|
119
120
|
def check_forbidden(path_info)
|
|
120
121
|
return unless path_info.include? ".."
|
|
121
|
-
|
|
122
|
+
|
|
123
|
+
expanded_path = ::File.expand_path(::File.join(@root, path_info))
|
|
124
|
+
return if expanded_path == @root || expanded_path.start_with?(@root_with_separator)
|
|
122
125
|
|
|
123
126
|
body = "Forbidden\n"
|
|
124
127
|
[403, { CONTENT_TYPE => "text/plain",
|
data/lib/rack/events.rb
CHANGED
|
@@ -29,12 +29,13 @@ module Rack
|
|
|
29
29
|
#
|
|
30
30
|
# * on_send(request, response)
|
|
31
31
|
#
|
|
32
|
-
# The webserver has started iterating over the response body
|
|
33
|
-
# has started sending data over the
|
|
34
|
-
# a request object and the response
|
|
35
|
-
# constructed from the rack triple that the
|
|
36
|
-
# SHOULD NOT be made to the response object
|
|
37
|
-
# started sending data. Any mutations will
|
|
32
|
+
# The webserver has started iterating over the response body, or has called
|
|
33
|
+
# the streaming body, and presumably has started sending data over the
|
|
34
|
+
# wire. This method is always called with a request object and the response
|
|
35
|
+
# object. The response object is constructed from the rack triple that the
|
|
36
|
+
# application returned. Changes SHOULD NOT be made to the response object
|
|
37
|
+
# as the webserver has already started sending data. Any mutations will
|
|
38
|
+
# likely result in an exception.
|
|
38
39
|
#
|
|
39
40
|
# * on_finish(request, response)
|
|
40
41
|
#
|
|
@@ -90,6 +91,20 @@ module Rack
|
|
|
90
91
|
@handlers.reverse_each { |handler| handler.on_send request, response }
|
|
91
92
|
super
|
|
92
93
|
end
|
|
94
|
+
|
|
95
|
+
def call(stream)
|
|
96
|
+
@handlers.reverse_each { |handler| handler.on_send request, response }
|
|
97
|
+
super
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def respond_to?(method_name, include_all = false)
|
|
101
|
+
case method_name
|
|
102
|
+
when :each, :call
|
|
103
|
+
@body.respond_to?(method_name, include_all)
|
|
104
|
+
else
|
|
105
|
+
super
|
|
106
|
+
end
|
|
107
|
+
end
|
|
93
108
|
end
|
|
94
109
|
|
|
95
110
|
class BufferedResponse < Rack::Response::Raw # :nodoc:
|
data/lib/rack/files.rb
CHANGED
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
|
-
|
|
19
|
-
|
|
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
|