rack 2.0.9.3 → 3.0.0
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 +808 -0
- data/CONTRIBUTING.md +142 -0
- data/{COPYING → MIT-LICENSE} +4 -2
- data/README.md +293 -0
- data/SPEC.rdoc +340 -0
- data/lib/rack/auth/abstract/handler.rb +6 -2
- data/lib/rack/auth/abstract/request.rb +4 -2
- data/lib/rack/auth/basic.rb +7 -4
- data/lib/rack/auth/digest/md5.rb +1 -129
- data/lib/rack/auth/digest/nonce.rb +1 -51
- data/lib/rack/auth/digest/params.rb +1 -52
- data/lib/rack/auth/digest/request.rb +1 -41
- data/lib/rack/auth/digest.rb +256 -0
- data/lib/rack/body_proxy.rb +18 -15
- data/lib/rack/builder.rb +151 -40
- data/lib/rack/cascade.rb +30 -12
- data/lib/rack/chunked.rb +74 -23
- data/lib/rack/common_logger.rb +49 -36
- data/lib/rack/conditional_get.rb +33 -26
- data/lib/rack/config.rb +2 -0
- data/lib/rack/constants.rb +63 -0
- data/lib/rack/content_length.rb +13 -16
- data/lib/rack/content_type.rb +12 -8
- data/lib/rack/deflater.rb +84 -45
- data/lib/rack/directory.rb +90 -64
- data/lib/rack/etag.rb +17 -23
- data/lib/rack/events.rb +23 -20
- data/lib/rack/file.rb +5 -172
- data/lib/rack/files.rb +216 -0
- data/lib/rack/head.rb +10 -9
- data/lib/rack/headers.rb +154 -0
- data/lib/rack/lint.rb +786 -645
- data/lib/rack/lock.rb +4 -6
- data/lib/rack/logger.rb +4 -0
- data/lib/rack/media_type.rb +10 -5
- data/lib/rack/method_override.rb +8 -2
- data/lib/rack/mime.rb +17 -1
- data/lib/rack/mock.rb +2 -195
- data/lib/rack/mock_request.rb +166 -0
- data/lib/rack/mock_response.rb +126 -0
- data/lib/rack/multipart/generator.rb +21 -15
- data/lib/rack/multipart/parser.rb +161 -118
- data/lib/rack/multipart/uploaded_file.rb +19 -7
- data/lib/rack/multipart.rb +23 -41
- data/lib/rack/null_logger.rb +11 -0
- data/lib/rack/query_parser.rb +126 -65
- data/lib/rack/recursive.rb +9 -5
- data/lib/rack/reloader.rb +6 -4
- data/lib/rack/request.rb +331 -74
- data/lib/rack/response.rb +223 -70
- data/lib/rack/rewindable_input.rb +28 -8
- data/lib/rack/runtime.rb +11 -8
- data/lib/rack/sendfile.rb +42 -33
- data/lib/rack/show_exceptions.rb +35 -18
- data/lib/rack/show_status.rb +25 -15
- data/lib/rack/static.rb +30 -18
- data/lib/rack/tempfile_reaper.rb +16 -5
- data/lib/rack/urlmap.rb +14 -6
- data/lib/rack/utils.rb +268 -260
- data/lib/rack/version.rb +34 -0
- data/lib/rack.rb +15 -92
- metadata +44 -207
- data/HISTORY.md +0 -520
- data/README.rdoc +0 -316
- data/Rakefile +0 -116
- data/SPEC +0 -263
- data/bin/rackup +0 -4
- 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 -4
- data/example/protectedlobster.rb +0 -14
- data/example/protectedlobster.ru +0 -8
- data/lib/rack/handler/cgi.rb +0 -60
- data/lib/rack/handler/fastcgi.rb +0 -100
- data/lib/rack/handler/lsws.rb +0 -61
- data/lib/rack/handler/scgi.rb +0 -70
- data/lib/rack/handler/thin.rb +0 -36
- data/lib/rack/handler/webrick.rb +0 -120
- data/lib/rack/handler.rb +0 -99
- data/lib/rack/lobster.rb +0 -70
- data/lib/rack/server.rb +0 -395
- data/lib/rack/session/abstract/id.rb +0 -510
- data/lib/rack/session/cookie.rb +0 -204
- data/lib/rack/session/memcache.rb +0 -99
- data/lib/rack/session/pool.rb +0 -83
- data/rack.gemspec +0 -34
- data/test/builder/an_underscore_app.rb +0 -5
- data/test/builder/anything.rb +0 -5
- data/test/builder/comment.ru +0 -4
- data/test/builder/end.ru +0 -5
- data/test/builder/line.ru +0 -1
- data/test/builder/options.ru +0 -2
- data/test/cgi/assets/folder/test.js +0 -1
- data/test/cgi/assets/fonts/font.eot +0 -1
- data/test/cgi/assets/images/image.png +0 -1
- data/test/cgi/assets/index.html +0 -1
- data/test/cgi/assets/javascripts/app.js +0 -1
- data/test/cgi/assets/stylesheets/app.css +0 -1
- data/test/cgi/lighttpd.conf +0 -26
- data/test/cgi/rackup_stub.rb +0 -6
- data/test/cgi/sample_rackup.ru +0 -5
- data/test/cgi/test +0 -9
- data/test/cgi/test+directory/test+file +0 -1
- data/test/cgi/test.fcgi +0 -9
- data/test/cgi/test.gz +0 -0
- data/test/cgi/test.ru +0 -5
- data/test/gemloader.rb +0 -10
- data/test/helper.rb +0 -34
- data/test/multipart/bad_robots +0 -259
- data/test/multipart/binary +0 -0
- data/test/multipart/content_type_and_no_filename +0 -6
- data/test/multipart/empty +0 -10
- data/test/multipart/fail_16384_nofile +0 -814
- data/test/multipart/file1.txt +0 -1
- data/test/multipart/filename_and_modification_param +0 -7
- data/test/multipart/filename_and_no_name +0 -6
- data/test/multipart/filename_with_encoded_words +0 -7
- data/test/multipart/filename_with_escaped_quotes +0 -6
- data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
- data/test/multipart/filename_with_null_byte +0 -7
- data/test/multipart/filename_with_percent_escaped_quotes +0 -6
- data/test/multipart/filename_with_single_quote +0 -7
- data/test/multipart/filename_with_unescaped_percentages +0 -6
- data/test/multipart/filename_with_unescaped_percentages2 +0 -6
- data/test/multipart/filename_with_unescaped_percentages3 +0 -6
- data/test/multipart/filename_with_unescaped_quotes +0 -6
- data/test/multipart/ie +0 -6
- data/test/multipart/invalid_character +0 -6
- data/test/multipart/mixed_files +0 -21
- data/test/multipart/nested +0 -10
- data/test/multipart/none +0 -9
- data/test/multipart/quoted +0 -15
- data/test/multipart/rack-logo.png +0 -0
- data/test/multipart/semicolon +0 -6
- data/test/multipart/text +0 -15
- data/test/multipart/three_files_three_fields +0 -31
- data/test/multipart/unity3d_wwwform +0 -11
- data/test/multipart/webkit +0 -32
- data/test/rackup/config.ru +0 -31
- data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
- data/test/spec_auth_basic.rb +0 -89
- data/test/spec_auth_digest.rb +0 -260
- data/test/spec_body_proxy.rb +0 -85
- data/test/spec_builder.rb +0 -233
- data/test/spec_cascade.rb +0 -63
- data/test/spec_cgi.rb +0 -84
- data/test/spec_chunked.rb +0 -103
- data/test/spec_common_logger.rb +0 -107
- data/test/spec_conditional_get.rb +0 -103
- data/test/spec_config.rb +0 -23
- data/test/spec_content_length.rb +0 -86
- data/test/spec_content_type.rb +0 -46
- data/test/spec_deflater.rb +0 -375
- data/test/spec_directory.rb +0 -148
- data/test/spec_etag.rb +0 -108
- data/test/spec_events.rb +0 -133
- data/test/spec_fastcgi.rb +0 -85
- data/test/spec_file.rb +0 -264
- data/test/spec_handler.rb +0 -57
- data/test/spec_head.rb +0 -46
- data/test/spec_lint.rb +0 -520
- data/test/spec_lobster.rb +0 -59
- data/test/spec_lock.rb +0 -204
- data/test/spec_logger.rb +0 -24
- data/test/spec_media_type.rb +0 -42
- data/test/spec_method_override.rb +0 -110
- data/test/spec_mime.rb +0 -51
- data/test/spec_mock.rb +0 -359
- data/test/spec_multipart.rb +0 -721
- data/test/spec_null_logger.rb +0 -21
- data/test/spec_recursive.rb +0 -75
- data/test/spec_request.rb +0 -1423
- data/test/spec_response.rb +0 -528
- data/test/spec_rewindable_input.rb +0 -128
- data/test/spec_runtime.rb +0 -50
- data/test/spec_sendfile.rb +0 -125
- data/test/spec_server.rb +0 -193
- data/test/spec_session_abstract_id.rb +0 -31
- data/test/spec_session_abstract_session_hash.rb +0 -45
- data/test/spec_session_cookie.rb +0 -442
- data/test/spec_session_memcache.rb +0 -357
- data/test/spec_session_persisted_secure_secure_session_hash.rb +0 -73
- data/test/spec_session_pool.rb +0 -247
- data/test/spec_show_exceptions.rb +0 -93
- data/test/spec_show_status.rb +0 -104
- data/test/spec_static.rb +0 -184
- data/test/spec_tempfile_reaper.rb +0 -64
- data/test/spec_thin.rb +0 -96
- data/test/spec_urlmap.rb +0 -237
- data/test/spec_utils.rb +0 -742
- data/test/spec_version.rb +0 -11
- data/test/spec_webrick.rb +0 -206
- data/test/static/another/index.html +0 -1
- data/test/static/foo.html +0 -1
- data/test/static/index.html +0 -1
- data/test/testrequest.rb +0 -78
- data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
- data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
data/lib/rack/request.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'constants'
|
4
|
+
require_relative 'utils'
|
5
|
+
require_relative 'media_type'
|
3
6
|
|
4
7
|
module Rack
|
5
8
|
# Rack::Request provides a convenient interface to a Rack
|
@@ -11,11 +14,54 @@ module Rack
|
|
11
14
|
# req.params["data"]
|
12
15
|
|
13
16
|
class Request
|
14
|
-
|
17
|
+
class << self
|
18
|
+
attr_accessor :ip_filter
|
19
|
+
|
20
|
+
# The priority when checking forwarded headers. The default
|
21
|
+
# is <tt>[:forwarded, :x_forwarded]</tt>, which means, check the
|
22
|
+
# +Forwarded+ header first, followed by the appropriate
|
23
|
+
# <tt>X-Forwarded-*</tt> header. You can revert the priority by
|
24
|
+
# reversing the priority, or remove checking of either
|
25
|
+
# or both headers by removing elements from the array.
|
26
|
+
#
|
27
|
+
# This should be set as appropriate in your environment
|
28
|
+
# based on what reverse proxies are in use. If you are not
|
29
|
+
# using reverse proxies, you should probably use an empty
|
30
|
+
# array.
|
31
|
+
attr_accessor :forwarded_priority
|
32
|
+
|
33
|
+
# The priority when checking either the <tt>X-Forwarded-Proto</tt>
|
34
|
+
# or <tt>X-Forwarded-Scheme</tt> header for the forwarded protocol.
|
35
|
+
# The default is <tt>[:proto, :scheme]</tt>, to try the
|
36
|
+
# <tt>X-Forwarded-Proto</tt> header before the
|
37
|
+
# <tt>X-Forwarded-Scheme</tt> header. Rack 2 had behavior
|
38
|
+
# similar to <tt>[:scheme, :proto]</tt>. You can remove either or
|
39
|
+
# both of the entries in array to ignore that respective header.
|
40
|
+
attr_accessor :x_forwarded_proto_priority
|
41
|
+
end
|
42
|
+
|
43
|
+
@forwarded_priority = [:forwarded, :x_forwarded]
|
44
|
+
@x_forwarded_proto_priority = [:proto, :scheme]
|
45
|
+
|
46
|
+
valid_ipv4_octet = /\.(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])/
|
47
|
+
|
48
|
+
trusted_proxies = Regexp.union(
|
49
|
+
/\A127#{valid_ipv4_octet}{3}\z/, # localhost IPv4 range 127.x.x.x, per RFC-3330
|
50
|
+
/\A::1\z/, # localhost IPv6 ::1
|
51
|
+
/\Af[cd][0-9a-f]{2}(?::[0-9a-f]{0,4}){0,7}\z/i, # private IPv6 range fc00 .. fdff
|
52
|
+
/\A10#{valid_ipv4_octet}{3}\z/, # private IPv4 range 10.x.x.x
|
53
|
+
/\A172\.(1[6-9]|2[0-9]|3[01])#{valid_ipv4_octet}{2}\z/, # private IPv4 range 172.16.0.0 .. 172.31.255.255
|
54
|
+
/\A192\.168#{valid_ipv4_octet}{2}\z/, # private IPv4 range 192.168.x.x
|
55
|
+
/\Alocalhost\z|\Aunix(\z|:)/i, # localhost hostname, and unix domain sockets
|
56
|
+
)
|
57
|
+
|
58
|
+
self.ip_filter = lambda { |ip| trusted_proxies.match?(ip) }
|
59
|
+
|
60
|
+
ALLOWED_SCHEMES = %w(https http wss ws).freeze
|
15
61
|
|
16
62
|
def initialize(env)
|
63
|
+
@env = env
|
17
64
|
@params = nil
|
18
|
-
super(env)
|
19
65
|
end
|
20
66
|
|
21
67
|
def params
|
@@ -39,6 +85,8 @@ module Rack
|
|
39
85
|
|
40
86
|
def initialize(env)
|
41
87
|
@env = env
|
88
|
+
# This module is included at least in `ActionDispatch::Request`
|
89
|
+
# The call to `super()` allows additional mixed-in initializers are called
|
42
90
|
super()
|
43
91
|
end
|
44
92
|
|
@@ -78,7 +126,7 @@ module Rack
|
|
78
126
|
# assert_equal 'image/png,*/*', request.get_header('Accept')
|
79
127
|
#
|
80
128
|
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
|
81
|
-
def add_header
|
129
|
+
def add_header(key, v)
|
82
130
|
if v.nil?
|
83
131
|
get_header key
|
84
132
|
elsif has_header? key
|
@@ -100,7 +148,7 @@ module Rack
|
|
100
148
|
|
101
149
|
module Helpers
|
102
150
|
# The set of form-data media-types. Requests that do not indicate
|
103
|
-
# one of the media types
|
151
|
+
# one of the media types present in this list will not be eligible
|
104
152
|
# for form-data / param parsing.
|
105
153
|
FORM_DATA_MEDIA_TYPES = [
|
106
154
|
'application/x-www-form-urlencoded',
|
@@ -108,7 +156,7 @@ module Rack
|
|
108
156
|
]
|
109
157
|
|
110
158
|
# The set of media-types. Requests that do not indicate
|
111
|
-
# one of the media types
|
159
|
+
# one of the media types present in this list will not be eligible
|
112
160
|
# for param parsing like soap attachments or generic multiparts
|
113
161
|
PARSEABLE_DATA_MEDIA_TYPES = [
|
114
162
|
'multipart/related',
|
@@ -119,11 +167,25 @@ module Rack
|
|
119
167
|
# to include the port in a generated URI.
|
120
168
|
DEFAULT_PORTS = { 'http' => 80, 'https' => 443, 'coffee' => 80 }
|
121
169
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
170
|
+
# The address of the client which connected to the proxy.
|
171
|
+
HTTP_X_FORWARDED_FOR = 'HTTP_X_FORWARDED_FOR'
|
172
|
+
|
173
|
+
# The contents of the host/:authority header sent to the proxy.
|
174
|
+
HTTP_X_FORWARDED_HOST = 'HTTP_X_FORWARDED_HOST'
|
175
|
+
|
176
|
+
HTTP_FORWARDED = 'HTTP_FORWARDED'
|
177
|
+
|
178
|
+
# The value of the scheme sent to the proxy.
|
179
|
+
HTTP_X_FORWARDED_SCHEME = 'HTTP_X_FORWARDED_SCHEME'
|
180
|
+
|
181
|
+
# The protocol used to connect to the proxy.
|
182
|
+
HTTP_X_FORWARDED_PROTO = 'HTTP_X_FORWARDED_PROTO'
|
183
|
+
|
184
|
+
# The port used to connect to the proxy.
|
185
|
+
HTTP_X_FORWARDED_PORT = 'HTTP_X_FORWARDED_PORT'
|
186
|
+
|
187
|
+
# Another way for specifying https scheme was used.
|
188
|
+
HTTP_X_FORWARDED_SSL = 'HTTP_X_FORWARDED_SSL'
|
127
189
|
|
128
190
|
def body; get_header(RACK_INPUT) end
|
129
191
|
def script_name; get_header(SCRIPT_NAME).to_s end
|
@@ -137,7 +199,6 @@ module Rack
|
|
137
199
|
def content_length; get_header('CONTENT_LENGTH') end
|
138
200
|
def logger; get_header(RACK_LOGGER) end
|
139
201
|
def user_agent; get_header('HTTP_USER_AGENT') end
|
140
|
-
def multithread?; get_header(RACK_MULTITHREAD) end
|
141
202
|
|
142
203
|
# the referer of the client
|
143
204
|
def referer; get_header('HTTP_REFERER') end
|
@@ -159,10 +220,10 @@ module Rack
|
|
159
220
|
def delete?; request_method == DELETE end
|
160
221
|
|
161
222
|
# Checks the HTTP request method (or verb) to see if it was of type GET
|
162
|
-
def get?; request_method == GET
|
223
|
+
def get?; request_method == GET end
|
163
224
|
|
164
225
|
# Checks the HTTP request method (or verb) to see if it was of type HEAD
|
165
|
-
def head?; request_method == HEAD
|
226
|
+
def head?; request_method == HEAD end
|
166
227
|
|
167
228
|
# Checks the HTTP request method (or verb) to see if it was of type OPTIONS
|
168
229
|
def options?; request_method == OPTIONS end
|
@@ -197,19 +258,50 @@ module Rack
|
|
197
258
|
end
|
198
259
|
end
|
199
260
|
|
261
|
+
# The authority of the incoming request as defined by RFC3976.
|
262
|
+
# https://tools.ietf.org/html/rfc3986#section-3.2
|
263
|
+
#
|
264
|
+
# In HTTP/1, this is the `host` header.
|
265
|
+
# In HTTP/2, this is the `:authority` pseudo-header.
|
200
266
|
def authority
|
201
|
-
|
267
|
+
forwarded_authority || host_authority || server_authority
|
268
|
+
end
|
269
|
+
|
270
|
+
# The authority as defined by the `SERVER_NAME` and `SERVER_PORT`
|
271
|
+
# variables.
|
272
|
+
def server_authority
|
273
|
+
host = self.server_name
|
274
|
+
port = self.server_port
|
275
|
+
|
276
|
+
if host
|
277
|
+
if port
|
278
|
+
"#{host}:#{port}"
|
279
|
+
else
|
280
|
+
host
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
def server_name
|
286
|
+
get_header(SERVER_NAME)
|
287
|
+
end
|
288
|
+
|
289
|
+
def server_port
|
290
|
+
get_header(SERVER_PORT)
|
202
291
|
end
|
203
292
|
|
204
293
|
def cookies
|
205
|
-
hash = fetch_header(RACK_REQUEST_COOKIE_HASH) do |
|
206
|
-
set_header(
|
294
|
+
hash = fetch_header(RACK_REQUEST_COOKIE_HASH) do |key|
|
295
|
+
set_header(key, {})
|
296
|
+
end
|
297
|
+
|
298
|
+
string = get_header(HTTP_COOKIE)
|
299
|
+
|
300
|
+
unless string == get_header(RACK_REQUEST_COOKIE_STRING)
|
301
|
+
hash.replace Utils.parse_cookies_header(string)
|
302
|
+
set_header(RACK_REQUEST_COOKIE_STRING, string)
|
207
303
|
end
|
208
|
-
string = get_header HTTP_COOKIE
|
209
304
|
|
210
|
-
return hash if string == get_header(RACK_REQUEST_COOKIE_STRING)
|
211
|
-
hash.replace Utils.parse_cookies_header get_header HTTP_COOKIE
|
212
|
-
set_header(RACK_REQUEST_COOKIE_STRING, string)
|
213
305
|
hash
|
214
306
|
end
|
215
307
|
|
@@ -222,46 +314,122 @@ module Rack
|
|
222
314
|
get_header("HTTP_X_REQUESTED_WITH") == "XMLHttpRequest"
|
223
315
|
end
|
224
316
|
|
225
|
-
|
226
|
-
|
227
|
-
|
317
|
+
# The `HTTP_HOST` header.
|
318
|
+
def host_authority
|
319
|
+
get_header(HTTP_HOST)
|
320
|
+
end
|
321
|
+
|
322
|
+
def host_with_port(authority = self.authority)
|
323
|
+
host, _, port = split_authority(authority)
|
324
|
+
|
325
|
+
if port == DEFAULT_PORTS[self.scheme]
|
326
|
+
host
|
228
327
|
else
|
229
|
-
|
328
|
+
authority
|
230
329
|
end
|
231
330
|
end
|
232
331
|
|
332
|
+
# Returns a formatted host, suitable for being used in a URI.
|
233
333
|
def host
|
234
|
-
|
235
|
-
|
334
|
+
split_authority(self.authority)[0]
|
335
|
+
end
|
336
|
+
|
337
|
+
# Returns an address suitable for being to resolve to an address.
|
338
|
+
# In the case of a domain name or IPv4 address, the result is the same
|
339
|
+
# as +host+. In the case of IPv6 or future address formats, the square
|
340
|
+
# brackets are removed.
|
341
|
+
def hostname
|
342
|
+
split_authority(self.authority)[1]
|
236
343
|
end
|
237
344
|
|
238
345
|
def port
|
239
|
-
if
|
240
|
-
port
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
346
|
+
if authority = self.authority
|
347
|
+
_, _, port = split_authority(authority)
|
348
|
+
end
|
349
|
+
|
350
|
+
port || forwarded_port&.last || DEFAULT_PORTS[scheme] || server_port
|
351
|
+
end
|
352
|
+
|
353
|
+
def forwarded_for
|
354
|
+
forwarded_priority.each do |type|
|
355
|
+
case type
|
356
|
+
when :forwarded
|
357
|
+
if forwarded_for = get_http_forwarded(:for)
|
358
|
+
return(forwarded_for.map! do |authority|
|
359
|
+
split_authority(authority)[1]
|
360
|
+
end)
|
361
|
+
end
|
362
|
+
when :x_forwarded
|
363
|
+
if value = get_header(HTTP_X_FORWARDED_FOR)
|
364
|
+
return(split_header(value).map do |authority|
|
365
|
+
split_authority(wrap_ipv6(authority))[1]
|
366
|
+
end)
|
367
|
+
end
|
368
|
+
end
|
249
369
|
end
|
370
|
+
|
371
|
+
nil
|
372
|
+
end
|
373
|
+
|
374
|
+
def forwarded_port
|
375
|
+
forwarded_priority.each do |type|
|
376
|
+
case type
|
377
|
+
when :forwarded
|
378
|
+
if forwarded = get_http_forwarded(:for)
|
379
|
+
return(forwarded.map do |authority|
|
380
|
+
split_authority(authority)[2]
|
381
|
+
end.compact)
|
382
|
+
end
|
383
|
+
when :x_forwarded
|
384
|
+
if value = get_header(HTTP_X_FORWARDED_PORT)
|
385
|
+
return split_header(value).map(&:to_i)
|
386
|
+
end
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
nil
|
391
|
+
end
|
392
|
+
|
393
|
+
def forwarded_authority
|
394
|
+
forwarded_priority.each do |type|
|
395
|
+
case type
|
396
|
+
when :forwarded
|
397
|
+
if forwarded = get_http_forwarded(:host)
|
398
|
+
return forwarded.last
|
399
|
+
end
|
400
|
+
when :x_forwarded
|
401
|
+
if value = get_header(HTTP_X_FORWARDED_HOST)
|
402
|
+
return wrap_ipv6(split_header(value).last)
|
403
|
+
end
|
404
|
+
end
|
405
|
+
end
|
406
|
+
|
407
|
+
nil
|
250
408
|
end
|
251
409
|
|
252
410
|
def ssl?
|
253
|
-
scheme == 'https'
|
411
|
+
scheme == 'https' || scheme == 'wss'
|
254
412
|
end
|
255
413
|
|
256
414
|
def ip
|
257
|
-
|
258
|
-
|
415
|
+
remote_addresses = split_header(get_header('REMOTE_ADDR'))
|
416
|
+
external_addresses = reject_trusted_ip_addresses(remote_addresses)
|
259
417
|
|
260
|
-
|
418
|
+
unless external_addresses.empty?
|
419
|
+
return external_addresses.last
|
420
|
+
end
|
261
421
|
|
262
|
-
|
422
|
+
if (forwarded_for = self.forwarded_for) && !forwarded_for.empty?
|
423
|
+
# The forwarded for addresses are ordered: client, proxy1, proxy2.
|
424
|
+
# So we reject all the trusted addresses (proxy*) and return the
|
425
|
+
# last client. Or if we trust everyone, we just return the first
|
426
|
+
# address.
|
427
|
+
return reject_trusted_ip_addresses(forwarded_for).last || forwarded_for.first
|
428
|
+
end
|
263
429
|
|
264
|
-
|
430
|
+
# If all the addresses are trusted, and we aren't forwarded, just return
|
431
|
+
# the first remote address, which represents the source of the request.
|
432
|
+
remote_addresses.first
|
265
433
|
end
|
266
434
|
|
267
435
|
# The media type (type/subtype) portion of the CONTENT_TYPE header
|
@@ -292,16 +460,17 @@ module Rack
|
|
292
460
|
end
|
293
461
|
|
294
462
|
# Determine whether the request body contains form-data by checking
|
295
|
-
# the request
|
463
|
+
# the request content-type for one of the media-types:
|
296
464
|
# "application/x-www-form-urlencoded" or "multipart/form-data". The
|
297
465
|
# list of form-data media types can be modified through the
|
298
466
|
# +FORM_DATA_MEDIA_TYPES+ array.
|
299
467
|
#
|
300
468
|
# A request body is also assumed to contain form-data when no
|
301
|
-
#
|
469
|
+
# content-type header is provided and the request_method is POST.
|
302
470
|
def form_data?
|
303
471
|
type = media_type
|
304
472
|
meth = get_header(RACK_METHODOVERRIDE_ORIGINAL_METHOD) || get_header(REQUEST_METHOD)
|
473
|
+
|
305
474
|
(meth == POST && type.nil?) || FORM_DATA_MEDIA_TYPES.include?(type)
|
306
475
|
end
|
307
476
|
|
@@ -316,7 +485,7 @@ module Rack
|
|
316
485
|
if get_header(RACK_REQUEST_QUERY_STRING) == query_string
|
317
486
|
get_header(RACK_REQUEST_QUERY_HASH)
|
318
487
|
else
|
319
|
-
query_hash = parse_query(query_string, '
|
488
|
+
query_hash = parse_query(query_string, '&')
|
320
489
|
set_header(RACK_REQUEST_QUERY_STRING, query_string)
|
321
490
|
set_header(RACK_REQUEST_QUERY_HASH, query_hash)
|
322
491
|
end
|
@@ -337,17 +506,16 @@ module Rack
|
|
337
506
|
|
338
507
|
# Fix for Safari Ajax postings that always append \0
|
339
508
|
# form_vars.sub!(/\0\z/, '') # performance replacement:
|
340
|
-
form_vars.slice!(-1) if form_vars
|
509
|
+
form_vars.slice!(-1) if form_vars.end_with?("\0")
|
341
510
|
|
342
511
|
set_header RACK_REQUEST_FORM_VARS, form_vars
|
343
512
|
set_header RACK_REQUEST_FORM_HASH, parse_query(form_vars, '&')
|
344
|
-
|
345
|
-
get_header(RACK_INPUT).rewind
|
346
513
|
end
|
347
514
|
set_header RACK_REQUEST_FORM_INPUT, get_header(RACK_INPUT)
|
348
515
|
get_header RACK_REQUEST_FORM_HASH
|
349
516
|
else
|
350
|
-
|
517
|
+
set_header RACK_REQUEST_FORM_INPUT, get_header(RACK_INPUT)
|
518
|
+
set_header(RACK_REQUEST_FORM_HASH, {})
|
351
519
|
end
|
352
520
|
end
|
353
521
|
|
@@ -356,8 +524,6 @@ module Rack
|
|
356
524
|
# Note that modifications will not be persisted in the env. Use update_param or delete_param if you want to destructively modify params.
|
357
525
|
def params
|
358
526
|
self.GET.merge(self.POST)
|
359
|
-
rescue EOFError
|
360
|
-
self.GET.dup
|
361
527
|
end
|
362
528
|
|
363
529
|
# Destructively update a parameter, whether it's in GET and/or POST. Returns nil.
|
@@ -386,13 +552,12 @@ module Rack
|
|
386
552
|
#
|
387
553
|
# <tt>env['rack.input']</tt> is not touched.
|
388
554
|
def delete_param(k)
|
389
|
-
|
555
|
+
post_value, get_value = self.POST.delete(k), self.GET.delete(k)
|
556
|
+
post_value || get_value
|
390
557
|
end
|
391
558
|
|
392
559
|
def base_url
|
393
|
-
|
394
|
-
url << ":#{port}" if port != DEFAULT_PORTS[scheme]
|
395
|
-
url
|
560
|
+
"#{scheme}://#{host_with_port}"
|
396
561
|
end
|
397
562
|
|
398
563
|
# Tries to return a remake of the original request URL as a string.
|
@@ -417,14 +582,12 @@ module Rack
|
|
417
582
|
end
|
418
583
|
|
419
584
|
def trusted_proxy?(ip)
|
420
|
-
ip
|
585
|
+
Rack::Request.ip_filter.call(ip)
|
421
586
|
end
|
422
587
|
|
423
588
|
# shortcut for <tt>request.params[key]</tt>
|
424
589
|
def [](key)
|
425
|
-
|
426
|
-
warn("Request#[] is deprecated and will be removed in a future version of Rack. Please use request.params[] instead")
|
427
|
-
end
|
590
|
+
warn("Request#[] is deprecated and will be removed in a future version of Rack. Please use request.params[] instead", uplevel: 1)
|
428
591
|
|
429
592
|
params[key.to_s]
|
430
593
|
end
|
@@ -433,9 +596,7 @@ module Rack
|
|
433
596
|
#
|
434
597
|
# Note that modifications will not be persisted in the env. Use update_param or delete_param if you want to destructively modify params.
|
435
598
|
def []=(key, value)
|
436
|
-
|
437
|
-
warn("Request#[]= is deprecated and will be removed in a future version of Rack. Please use request.params[]= instead")
|
438
|
-
end
|
599
|
+
warn("Request#[]= is deprecated and will be removed in a future version of Rack. Please use request.params[]= instead", uplevel: 1)
|
439
600
|
|
440
601
|
params[key.to_s] = value
|
441
602
|
end
|
@@ -449,6 +610,20 @@ module Rack
|
|
449
610
|
|
450
611
|
def default_session; {}; end
|
451
612
|
|
613
|
+
# Assist with compatibility when processing `X-Forwarded-For`.
|
614
|
+
def wrap_ipv6(host)
|
615
|
+
# Even thought IPv6 addresses should be wrapped in square brackets,
|
616
|
+
# sometimes this is not done in various legacy/underspecified headers.
|
617
|
+
# So we try to fix this situation for compatibility reasons.
|
618
|
+
|
619
|
+
# Try to detect IPv6 addresses which aren't escaped yet:
|
620
|
+
if !host.start_with?('[') && host.count(':') > 1
|
621
|
+
"[#{host}]"
|
622
|
+
else
|
623
|
+
host
|
624
|
+
end
|
625
|
+
end
|
626
|
+
|
452
627
|
def parse_http_accept_header(header)
|
453
628
|
header.to_s.split(/\s*,\s*/).map do |part|
|
454
629
|
attribute, parameters = part.split(/\s*;\s*/, 2)
|
@@ -460,11 +635,16 @@ module Rack
|
|
460
635
|
end
|
461
636
|
end
|
462
637
|
|
638
|
+
# Get an array of values set in the RFC 7239 `Forwarded` request header.
|
639
|
+
def get_http_forwarded(token)
|
640
|
+
Utils.forwarded_values(get_header(HTTP_FORWARDED))&.[](token)
|
641
|
+
end
|
642
|
+
|
463
643
|
def query_parser
|
464
644
|
Utils.default_query_parser
|
465
645
|
end
|
466
646
|
|
467
|
-
def parse_query(qs, d='&')
|
647
|
+
def parse_query(qs, d = '&')
|
468
648
|
query_parser.parse_nested_query(qs, d)
|
469
649
|
end
|
470
650
|
|
@@ -472,29 +652,106 @@ module Rack
|
|
472
652
|
Rack::Multipart.extract_multipart(self, query_parser)
|
473
653
|
end
|
474
654
|
|
475
|
-
def
|
476
|
-
|
655
|
+
def split_header(value)
|
656
|
+
value ? value.strip.split(/[,\s]+/) : []
|
657
|
+
end
|
658
|
+
|
659
|
+
# ipv6 extracted from resolv stdlib, simplified
|
660
|
+
# to remove numbered match group creation.
|
661
|
+
ipv6 = Regexp.union(
|
662
|
+
/(?:[0-9A-Fa-f]{1,4}:){7}
|
663
|
+
[0-9A-Fa-f]{1,4}/x,
|
664
|
+
/(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)? ::
|
665
|
+
(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?/x,
|
666
|
+
/(?:[0-9A-Fa-f]{1,4}:){6,6}
|
667
|
+
\d+\.\d+\.\d+\.\d+/x,
|
668
|
+
/(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)? ::
|
669
|
+
(?:[0-9A-Fa-f]{1,4}:)*
|
670
|
+
\d+\.\d+\.\d+\.\d+/x,
|
671
|
+
/[Ff][Ee]80
|
672
|
+
(?::[0-9A-Fa-f]{1,4}){7}
|
673
|
+
%[-0-9A-Za-z._~]+/x,
|
674
|
+
/[Ff][Ee]80:
|
675
|
+
(?:
|
676
|
+
(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)? ::
|
677
|
+
(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?
|
678
|
+
|
|
679
|
+
:(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?
|
680
|
+
)?
|
681
|
+
:[0-9A-Fa-f]{1,4}%[-0-9A-Za-z._~]+/x)
|
682
|
+
|
683
|
+
AUTHORITY = /
|
684
|
+
\A
|
685
|
+
(?<host>
|
686
|
+
# Match IPv6 as a string of hex digits and colons in square brackets
|
687
|
+
\[(?<address>#{ipv6})\]
|
688
|
+
|
|
689
|
+
# Match any other printable string (except square brackets) as a hostname
|
690
|
+
(?<address>[[[:graph:]&&[^\[\]]]]*?)
|
691
|
+
)
|
692
|
+
(:(?<port>\d+))?
|
693
|
+
\z
|
694
|
+
/x
|
695
|
+
|
696
|
+
private_constant :AUTHORITY
|
697
|
+
|
698
|
+
def split_authority(authority)
|
699
|
+
return [] if authority.nil?
|
700
|
+
return [] unless match = AUTHORITY.match(authority)
|
701
|
+
return match[:host], match[:address], match[:port]&.to_i
|
477
702
|
end
|
478
703
|
|
479
704
|
def reject_trusted_ip_addresses(ip_addresses)
|
480
705
|
ip_addresses.reject { |ip| trusted_proxy?(ip) }
|
481
706
|
end
|
482
707
|
|
708
|
+
FORWARDED_SCHEME_HEADERS = {
|
709
|
+
proto: HTTP_X_FORWARDED_PROTO,
|
710
|
+
scheme: HTTP_X_FORWARDED_SCHEME
|
711
|
+
}.freeze
|
712
|
+
private_constant :FORWARDED_SCHEME_HEADERS
|
483
713
|
def forwarded_scheme
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
714
|
+
forwarded_priority.each do |type|
|
715
|
+
case type
|
716
|
+
when :forwarded
|
717
|
+
if (forwarded_proto = get_http_forwarded(:proto)) &&
|
718
|
+
(scheme = allowed_scheme(forwarded_proto.last))
|
719
|
+
return scheme
|
720
|
+
end
|
721
|
+
when :x_forwarded
|
722
|
+
x_forwarded_proto_priority.each do |x_type|
|
723
|
+
if header = FORWARDED_SCHEME_HEADERS[x_type]
|
724
|
+
split_header(get_header(header)).reverse_each do |scheme|
|
725
|
+
if allowed_scheme(scheme)
|
726
|
+
return scheme
|
727
|
+
end
|
728
|
+
end
|
729
|
+
end
|
730
|
+
end
|
731
|
+
end
|
491
732
|
end
|
492
733
|
|
493
734
|
nil
|
494
735
|
end
|
736
|
+
|
737
|
+
def allowed_scheme(header)
|
738
|
+
header if ALLOWED_SCHEMES.include?(header)
|
739
|
+
end
|
740
|
+
|
741
|
+
def forwarded_priority
|
742
|
+
Request.forwarded_priority
|
743
|
+
end
|
744
|
+
|
745
|
+
def x_forwarded_proto_priority
|
746
|
+
Request.x_forwarded_proto_priority
|
747
|
+
end
|
495
748
|
end
|
496
749
|
|
497
750
|
include Env
|
498
751
|
include Helpers
|
499
752
|
end
|
500
753
|
end
|
754
|
+
|
755
|
+
# :nocov:
|
756
|
+
require_relative 'multipart' unless defined?(Rack::Multipart)
|
757
|
+
# :nocov:
|