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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +291 -78
- data/CONTRIBUTING.md +63 -55
- data/MIT-LICENSE +1 -1
- data/README.md +328 -0
- data/SPEC.rdoc +213 -136
- data/lib/rack/auth/abstract/handler.rb +3 -1
- data/lib/rack/auth/abstract/request.rb +3 -1
- data/lib/rack/auth/basic.rb +1 -4
- data/lib/rack/bad_request.rb +8 -0
- data/lib/rack/body_proxy.rb +21 -3
- data/lib/rack/builder.rb +102 -69
- data/lib/rack/cascade.rb +2 -3
- data/lib/rack/common_logger.rb +23 -18
- data/lib/rack/conditional_get.rb +18 -15
- data/lib/rack/constants.rb +67 -0
- data/lib/rack/content_length.rb +12 -16
- data/lib/rack/content_type.rb +8 -5
- data/lib/rack/deflater.rb +40 -26
- data/lib/rack/directory.rb +9 -3
- data/lib/rack/etag.rb +14 -23
- data/lib/rack/events.rb +4 -0
- data/lib/rack/files.rb +15 -17
- data/lib/rack/head.rb +9 -8
- data/lib/rack/headers.rb +238 -0
- data/lib/rack/lint.rb +864 -681
- data/lib/rack/lock.rb +2 -5
- data/lib/rack/logger.rb +3 -0
- data/lib/rack/media_type.rb +9 -4
- data/lib/rack/method_override.rb +5 -1
- data/lib/rack/mime.rb +14 -5
- data/lib/rack/mock.rb +1 -271
- data/lib/rack/mock_request.rb +171 -0
- data/lib/rack/mock_response.rb +124 -0
- data/lib/rack/multipart/generator.rb +7 -5
- data/lib/rack/multipart/parser.rb +218 -91
- data/lib/rack/multipart/uploaded_file.rb +4 -0
- data/lib/rack/multipart.rb +53 -40
- data/lib/rack/null_logger.rb +9 -0
- data/lib/rack/query_parser.rb +81 -102
- data/lib/rack/recursive.rb +2 -0
- data/lib/rack/reloader.rb +0 -2
- data/lib/rack/request.rb +248 -123
- data/lib/rack/response.rb +146 -66
- data/lib/rack/rewindable_input.rb +24 -5
- data/lib/rack/runtime.rb +7 -6
- data/lib/rack/sendfile.rb +30 -25
- data/lib/rack/show_exceptions.rb +21 -4
- data/lib/rack/show_status.rb +17 -7
- data/lib/rack/static.rb +8 -8
- data/lib/rack/tempfile_reaper.rb +15 -4
- data/lib/rack/urlmap.rb +3 -1
- data/lib/rack/utils.rb +237 -235
- data/lib/rack/version.rb +1 -9
- data/lib/rack.rb +13 -89
- metadata +15 -41
- data/README.rdoc +0 -320
- data/Rakefile +0 -130
- data/bin/rackup +0 -5
- data/contrib/rack.png +0 -0
- data/contrib/rack.svg +0 -150
- data/contrib/rack_logo.svg +0 -164
- data/contrib/rdoc.css +0 -412
- data/example/lobster.ru +0 -6
- data/example/protectedlobster.rb +0 -16
- data/example/protectedlobster.ru +0 -10
- data/lib/rack/auth/digest/md5.rb +0 -131
- data/lib/rack/auth/digest/nonce.rb +0 -54
- data/lib/rack/auth/digest/params.rb +0 -54
- data/lib/rack/auth/digest/request.rb +0 -43
- data/lib/rack/chunked.rb +0 -117
- data/lib/rack/core_ext/regexp.rb +0 -14
- data/lib/rack/file.rb +0 -7
- data/lib/rack/handler/cgi.rb +0 -59
- data/lib/rack/handler/fastcgi.rb +0 -100
- data/lib/rack/handler/lsws.rb +0 -61
- data/lib/rack/handler/scgi.rb +0 -71
- data/lib/rack/handler/thin.rb +0 -36
- data/lib/rack/handler/webrick.rb +0 -129
- data/lib/rack/handler.rb +0 -104
- data/lib/rack/lobster.rb +0 -70
- data/lib/rack/server.rb +0 -466
- data/lib/rack/session/abstract/id.rb +0 -523
- data/lib/rack/session/cookie.rb +0 -203
- data/lib/rack/session/memcache.rb +0 -10
- data/lib/rack/session/pool.rb +0 -85
- data/rack.gemspec +0 -46
data/lib/rack/multipart.rb
CHANGED
@@ -1,64 +1,77 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'constants'
|
4
|
+
require_relative 'utils'
|
5
|
+
|
3
6
|
require_relative 'multipart/parser'
|
7
|
+
require_relative 'multipart/generator'
|
8
|
+
|
9
|
+
require_relative 'bad_request'
|
4
10
|
|
5
11
|
module Rack
|
6
12
|
# A multipart form data parser, adapted from IOWA.
|
7
13
|
#
|
8
14
|
# Usually, Rack::Request#POST takes care of calling this.
|
9
15
|
module Multipart
|
10
|
-
autoload :UploadedFile, 'rack/multipart/uploaded_file'
|
11
|
-
autoload :Generator, 'rack/multipart/generator'
|
12
|
-
|
13
|
-
EOL = "\r\n"
|
14
16
|
MULTIPART_BOUNDARY = "AaB03x"
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
17
|
+
|
18
|
+
class MissingInputError < StandardError
|
19
|
+
include BadRequest
|
20
|
+
end
|
21
|
+
|
22
|
+
# Accumulator for multipart form data, conforming to the QueryParser API.
|
23
|
+
# In future, the Parser could return the pair list directly, but that would
|
24
|
+
# change its API.
|
25
|
+
class ParamList # :nodoc:
|
26
|
+
def self.make_params
|
27
|
+
new
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.normalize_params(params, key, value)
|
31
|
+
params << [key, value]
|
32
|
+
end
|
33
|
+
|
34
|
+
def initialize
|
35
|
+
@pairs = []
|
36
|
+
end
|
37
|
+
|
38
|
+
def <<(pair)
|
39
|
+
@pairs << pair
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_params_hash
|
43
|
+
@pairs
|
44
|
+
end
|
45
|
+
end
|
38
46
|
|
39
47
|
class << self
|
40
48
|
def parse_multipart(env, params = Rack::Utils.default_query_parser)
|
41
|
-
|
42
|
-
|
49
|
+
unless io = env[RACK_INPUT]
|
50
|
+
raise MissingInputError, "Missing input stream!"
|
51
|
+
end
|
43
52
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
content_length = req.content_length
|
48
|
-
content_length = content_length.to_i if content_length
|
53
|
+
if content_length = env['CONTENT_LENGTH']
|
54
|
+
content_length = content_length.to_i
|
55
|
+
end
|
49
56
|
|
50
|
-
|
51
|
-
bufsize = req.get_header(RACK_MULTIPART_BUFFER_SIZE) || Parser::BUFSIZE
|
57
|
+
content_type = env['CONTENT_TYPE']
|
52
58
|
|
53
|
-
|
54
|
-
|
55
|
-
|
59
|
+
tempfile = env[RACK_MULTIPART_TEMPFILE_FACTORY] || Parser::TEMPFILE_FACTORY
|
60
|
+
bufsize = env[RACK_MULTIPART_BUFFER_SIZE] || Parser::BUFSIZE
|
61
|
+
|
62
|
+
info = Parser.parse(io, content_length, content_type, tempfile, bufsize, params)
|
63
|
+
env[RACK_TEMPFILES] = info.tmp_files
|
64
|
+
|
65
|
+
return info.params
|
66
|
+
end
|
67
|
+
|
68
|
+
def extract_multipart(request, params = Rack::Utils.default_query_parser)
|
69
|
+
parse_multipart(request.env)
|
56
70
|
end
|
57
71
|
|
58
72
|
def build_multipart(params, first = true)
|
59
73
|
Generator.new(params, first).dump
|
60
74
|
end
|
61
75
|
end
|
62
|
-
|
63
76
|
end
|
64
77
|
end
|
data/lib/rack/null_logger.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
|
class NullLogger
|
5
7
|
def initialize(app)
|
@@ -22,6 +24,11 @@ module Rack
|
|
22
24
|
def warn? ; end
|
23
25
|
def error? ; end
|
24
26
|
def fatal? ; end
|
27
|
+
def debug! ; end
|
28
|
+
def error! ; end
|
29
|
+
def fatal! ; end
|
30
|
+
def info! ; end
|
31
|
+
def warn! ; end
|
25
32
|
def level ; end
|
26
33
|
def progname ; end
|
27
34
|
def datetime_format ; end
|
@@ -34,6 +41,8 @@ module Rack
|
|
34
41
|
def sev_threshold=(sev_threshold); end
|
35
42
|
def close ; end
|
36
43
|
def add(severity, message = nil, progname = nil, &block); end
|
44
|
+
def log(severity, message = nil, progname = nil, &block); end
|
37
45
|
def <<(msg); end
|
46
|
+
def reopen(logdev = nil); end
|
38
47
|
end
|
39
48
|
end
|
data/lib/rack/query_parser.rb
CHANGED
@@ -1,48 +1,53 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'bad_request'
|
4
|
+
require 'uri'
|
5
|
+
|
3
6
|
module Rack
|
4
7
|
class QueryParser
|
5
|
-
|
6
|
-
|
7
|
-
DEFAULT_SEP = /[&;] */n
|
8
|
-
COMMON_SEP = { ";" => /[;] */n, ";," => /[;,] */n, "&" => /[&] */n }
|
8
|
+
DEFAULT_SEP = /& */n
|
9
|
+
COMMON_SEP = { ";" => /; */n, ";," => /[;,] */n, "&" => /& */n }
|
9
10
|
|
10
11
|
# ParameterTypeError is the error that is raised when incoming structural
|
11
12
|
# parameters (parsed by parse_nested_query) contain conflicting types.
|
12
|
-
class ParameterTypeError < TypeError
|
13
|
+
class ParameterTypeError < TypeError
|
14
|
+
include BadRequest
|
15
|
+
end
|
13
16
|
|
14
17
|
# InvalidParameterError is the error that is raised when incoming structural
|
15
18
|
# parameters (parsed by parse_nested_query) contain invalid format or byte
|
16
19
|
# sequence.
|
17
|
-
class InvalidParameterError < ArgumentError
|
20
|
+
class InvalidParameterError < ArgumentError
|
21
|
+
include BadRequest
|
22
|
+
end
|
18
23
|
|
19
24
|
# ParamsTooDeepError is the error that is raised when params are recursively
|
20
25
|
# nested over the specified limit.
|
21
|
-
class ParamsTooDeepError < RangeError
|
26
|
+
class ParamsTooDeepError < RangeError
|
27
|
+
include BadRequest
|
28
|
+
end
|
22
29
|
|
23
|
-
def self.make_default(
|
24
|
-
new Params,
|
30
|
+
def self.make_default(param_depth_limit)
|
31
|
+
new Params, param_depth_limit
|
25
32
|
end
|
26
33
|
|
27
|
-
attr_reader :
|
34
|
+
attr_reader :param_depth_limit
|
28
35
|
|
29
|
-
def initialize(params_class,
|
36
|
+
def initialize(params_class, param_depth_limit)
|
30
37
|
@params_class = params_class
|
31
|
-
@key_space_limit = key_space_limit
|
32
38
|
@param_depth_limit = param_depth_limit
|
33
39
|
end
|
34
40
|
|
35
41
|
# Stolen from Mongrel, with some small modifications:
|
36
|
-
# Parses a query string by breaking it up at the '&'
|
37
|
-
#
|
38
|
-
#
|
39
|
-
|
40
|
-
def parse_query(qs, d = nil, &unescaper)
|
42
|
+
# Parses a query string by breaking it up at the '&'. You can also use this
|
43
|
+
# to parse cookies by changing the characters used in the second parameter
|
44
|
+
# (which defaults to '&').
|
45
|
+
def parse_query(qs, separator = nil, &unescaper)
|
41
46
|
unescaper ||= method(:unescape)
|
42
47
|
|
43
48
|
params = make_params
|
44
49
|
|
45
|
-
(qs || '').split(
|
50
|
+
(qs || '').split(separator ? (COMMON_SEP[separator] || /[#{separator}] */n) : DEFAULT_SEP).each do |p|
|
46
51
|
next if p.empty?
|
47
52
|
k, v = p.split('=', 2).map!(&unescaper)
|
48
53
|
|
@@ -65,14 +70,14 @@ module Rack
|
|
65
70
|
# query strings with parameters of conflicting types, in this case a
|
66
71
|
# ParameterTypeError is raised. Users are encouraged to return a 400 in this
|
67
72
|
# case.
|
68
|
-
def parse_nested_query(qs,
|
73
|
+
def parse_nested_query(qs, separator = nil)
|
69
74
|
params = make_params
|
70
75
|
|
71
76
|
unless qs.nil? || qs.empty?
|
72
|
-
(qs || '').split(
|
77
|
+
(qs || '').split(separator ? (COMMON_SEP[separator] || /[#{separator}] */n) : DEFAULT_SEP).each do |p|
|
73
78
|
k, v = p.split('=', 2).map! { |s| unescape(s) }
|
74
79
|
|
75
|
-
|
80
|
+
_normalize_params(params, k, v, 0)
|
76
81
|
end
|
77
82
|
end
|
78
83
|
|
@@ -83,58 +88,87 @@ module Rack
|
|
83
88
|
|
84
89
|
# normalize_params recursively expands parameters into structural types. If
|
85
90
|
# the structural types represented by two different parameter names are in
|
86
|
-
# conflict, a ParameterTypeError is raised.
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
name
|
91
|
-
|
92
|
-
after = $' || ''
|
91
|
+
# conflict, a ParameterTypeError is raised. The depth argument is deprecated
|
92
|
+
# and should no longer be used, it is kept for backwards compatibility with
|
93
|
+
# earlier versions of rack.
|
94
|
+
def normalize_params(params, name, v, _depth=nil)
|
95
|
+
_normalize_params(params, name, v, 0)
|
96
|
+
end
|
93
97
|
|
94
|
-
|
95
|
-
|
96
|
-
|
98
|
+
private def _normalize_params(params, name, v, depth)
|
99
|
+
raise ParamsTooDeepError if depth >= param_depth_limit
|
100
|
+
|
101
|
+
if !name
|
102
|
+
# nil name, treat same as empty string (required by tests)
|
103
|
+
k = after = ''
|
104
|
+
elsif depth == 0
|
105
|
+
# Start of parsing, don't treat [] or [ at start of string specially
|
106
|
+
if start = name.index('[', 1)
|
107
|
+
# Start of parameter nesting, use part before brackets as key
|
108
|
+
k = name[0, start]
|
109
|
+
after = name[start, name.length]
|
97
110
|
else
|
98
|
-
|
111
|
+
# Plain parameter with no nesting
|
112
|
+
k = name
|
113
|
+
after = ''
|
99
114
|
end
|
115
|
+
elsif name.start_with?('[]')
|
116
|
+
# Array nesting
|
117
|
+
k = '[]'
|
118
|
+
after = name[2, name.length]
|
119
|
+
elsif name.start_with?('[') && (start = name.index(']', 1))
|
120
|
+
# Hash nesting, use the part inside brackets as the key
|
121
|
+
k = name[1, start-1]
|
122
|
+
after = name[start+1, name.length]
|
123
|
+
else
|
124
|
+
# Probably malformed input, nested but not starting with [
|
125
|
+
# treat full name as key for backwards compatibility.
|
126
|
+
k = name
|
127
|
+
after = ''
|
100
128
|
end
|
101
129
|
|
130
|
+
return if k.empty?
|
131
|
+
|
102
132
|
if after == ''
|
103
|
-
|
133
|
+
if k == '[]' && depth != 0
|
134
|
+
return [v]
|
135
|
+
else
|
136
|
+
params[k] = v
|
137
|
+
end
|
104
138
|
elsif after == "["
|
105
139
|
params[name] = v
|
106
140
|
elsif after == "[]"
|
107
141
|
params[k] ||= []
|
108
142
|
raise ParameterTypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
|
109
143
|
params[k] << v
|
110
|
-
elsif after
|
111
|
-
|
144
|
+
elsif after.start_with?('[]')
|
145
|
+
# Recognize x[][y] (hash inside array) parameters
|
146
|
+
unless after[2] == '[' && after.end_with?(']') && (child_key = after[3, after.length-4]) && !child_key.empty? && !child_key.index('[') && !child_key.index(']')
|
147
|
+
# Handle other nested array parameters
|
148
|
+
child_key = after[2, after.length]
|
149
|
+
end
|
112
150
|
params[k] ||= []
|
113
151
|
raise ParameterTypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
|
114
152
|
if params_hash_type?(params[k].last) && !params_hash_has_key?(params[k].last, child_key)
|
115
|
-
|
153
|
+
_normalize_params(params[k].last, child_key, v, depth + 1)
|
116
154
|
else
|
117
|
-
params[k] <<
|
155
|
+
params[k] << _normalize_params(make_params, child_key, v, depth + 1)
|
118
156
|
end
|
119
157
|
else
|
120
158
|
params[k] ||= make_params
|
121
159
|
raise ParameterTypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params_hash_type?(params[k])
|
122
|
-
params[k] =
|
160
|
+
params[k] = _normalize_params(params[k], after, v, depth + 1)
|
123
161
|
end
|
124
162
|
|
125
163
|
params
|
126
164
|
end
|
127
165
|
|
128
166
|
def make_params
|
129
|
-
@params_class.new
|
130
|
-
end
|
131
|
-
|
132
|
-
def new_space_limit(key_space_limit)
|
133
|
-
self.class.new @params_class, key_space_limit, param_depth_limit
|
167
|
+
@params_class.new
|
134
168
|
end
|
135
169
|
|
136
170
|
def new_depth_limit(param_depth_limit)
|
137
|
-
self.class.new @params_class,
|
171
|
+
self.class.new @params_class, param_depth_limit
|
138
172
|
end
|
139
173
|
|
140
174
|
private
|
@@ -155,66 +189,11 @@ module Rack
|
|
155
189
|
true
|
156
190
|
end
|
157
191
|
|
158
|
-
def unescape(
|
159
|
-
|
192
|
+
def unescape(string, encoding = Encoding::UTF_8)
|
193
|
+
URI.decode_www_form_component(string, encoding)
|
160
194
|
end
|
161
195
|
|
162
|
-
class Params
|
163
|
-
def initialize(limit)
|
164
|
-
@limit = limit
|
165
|
-
@size = 0
|
166
|
-
@params = {}
|
167
|
-
end
|
168
|
-
|
169
|
-
def [](key)
|
170
|
-
@params[key]
|
171
|
-
end
|
172
|
-
|
173
|
-
def []=(key, value)
|
174
|
-
@size += key.size if key && !@params.key?(key)
|
175
|
-
raise ParamsTooDeepError, 'exceeded available parameter key space' if @size > @limit
|
176
|
-
@params[key] = value
|
177
|
-
end
|
178
|
-
|
179
|
-
def key?(key)
|
180
|
-
@params.key?(key)
|
181
|
-
end
|
182
|
-
|
183
|
-
# Recursively unwraps nested `Params` objects and constructs an object
|
184
|
-
# of the same shape, but using the objects' internal representations
|
185
|
-
# (Ruby hashes) in place of the objects. The result is a hash consisting
|
186
|
-
# purely of Ruby primitives.
|
187
|
-
#
|
188
|
-
# Mutation warning!
|
189
|
-
#
|
190
|
-
# 1. This method mutates the internal representation of the `Params`
|
191
|
-
# objects in order to save object allocations.
|
192
|
-
#
|
193
|
-
# 2. The value you get back is a reference to the internal hash
|
194
|
-
# representation, not a copy.
|
195
|
-
#
|
196
|
-
# 3. Because the `Params` object's internal representation is mutable
|
197
|
-
# through the `#[]=` method, it is not thread safe. The result of
|
198
|
-
# getting the hash representation while another thread is adding a
|
199
|
-
# key to it is non-deterministic.
|
200
|
-
#
|
201
|
-
def to_h
|
202
|
-
@params.each do |key, value|
|
203
|
-
case value
|
204
|
-
when self
|
205
|
-
# Handle circular references gracefully.
|
206
|
-
@params[key] = @params
|
207
|
-
when Params
|
208
|
-
@params[key] = value.to_h
|
209
|
-
when Array
|
210
|
-
value.map! { |v| v.kind_of?(Params) ? v.to_h : v }
|
211
|
-
else
|
212
|
-
# Ignore anything that is not a `Params` object or
|
213
|
-
# a collection that can contain one.
|
214
|
-
end
|
215
|
-
end
|
216
|
-
@params
|
217
|
-
end
|
196
|
+
class Params < Hash
|
218
197
|
alias_method :to_params_hash, :to_h
|
219
198
|
end
|
220
199
|
end
|
data/lib/rack/recursive.rb
CHANGED
data/lib/rack/reloader.rb
CHANGED
@@ -22,8 +22,6 @@ module Rack
|
|
22
22
|
# It is performing a check/reload cycle at the start of every request, but
|
23
23
|
# also respects a cool down time, during which nothing will be done.
|
24
24
|
class Reloader
|
25
|
-
(require_relative 'core_ext/regexp'; using ::Rack::RegexpExtensions) if RUBY_VERSION < '2.4'
|
26
|
-
|
27
25
|
def initialize(app, cooldown = 10, backend = Stat)
|
28
26
|
@app = app
|
29
27
|
@cooldown = cooldown
|