rack 2.0.9.3 → 2.2.8
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 +740 -0
- data/CONTRIBUTING.md +136 -0
- data/{COPYING → MIT-LICENSE} +4 -2
- data/README.rdoc +151 -147
- data/Rakefile +37 -23
- data/{SPEC → SPEC.rdoc} +44 -15
- data/bin/rackup +1 -0
- data/example/lobster.ru +2 -0
- data/example/protectedlobster.rb +3 -1
- data/example/protectedlobster.ru +2 -0
- data/lib/rack/auth/abstract/handler.rb +3 -1
- data/lib/rack/auth/abstract/request.rb +1 -1
- data/lib/rack/auth/basic.rb +7 -4
- data/lib/rack/auth/digest/md5.rb +13 -11
- data/lib/rack/auth/digest/nonce.rb +6 -3
- data/lib/rack/auth/digest/params.rb +4 -2
- data/lib/rack/auth/digest/request.rb +5 -3
- data/lib/rack/body_proxy.rb +15 -14
- data/lib/rack/builder.rb +116 -23
- data/lib/rack/cascade.rb +28 -12
- data/lib/rack/chunked.rb +68 -20
- data/lib/rack/common_logger.rb +33 -25
- data/lib/rack/conditional_get.rb +20 -16
- data/lib/rack/config.rb +2 -0
- data/lib/rack/content_length.rb +8 -7
- data/lib/rack/content_type.rb +5 -4
- data/lib/rack/core_ext/regexp.rb +14 -0
- data/lib/rack/deflater.rb +59 -34
- data/lib/rack/directory.rb +84 -64
- data/lib/rack/etag.rb +7 -4
- data/lib/rack/events.rb +19 -20
- data/lib/rack/file.rb +4 -173
- data/lib/rack/files.rb +218 -0
- data/lib/rack/handler/cgi.rb +2 -3
- data/lib/rack/handler/fastcgi.rb +4 -4
- data/lib/rack/handler/lsws.rb +3 -3
- data/lib/rack/handler/scgi.rb +9 -8
- data/lib/rack/handler/thin.rb +3 -3
- data/lib/rack/handler/webrick.rb +15 -6
- data/lib/rack/handler.rb +7 -2
- data/lib/rack/head.rb +1 -1
- data/lib/rack/lint.rb +219 -184
- data/lib/rack/lobster.rb +10 -10
- data/lib/rack/lock.rb +2 -1
- data/lib/rack/logger.rb +2 -0
- data/lib/rack/media_type.rb +10 -5
- data/lib/rack/method_override.rb +5 -3
- data/lib/rack/mime.rb +9 -1
- data/lib/rack/mock.rb +97 -20
- data/lib/rack/multipart/generator.rb +17 -13
- data/lib/rack/multipart/parser.rb +55 -56
- data/lib/rack/multipart/uploaded_file.rb +15 -7
- data/lib/rack/multipart.rb +4 -2
- data/lib/rack/null_logger.rb +2 -0
- data/lib/rack/query_parser.rb +59 -30
- data/lib/rack/recursive.rb +7 -5
- data/lib/rack/reloader.rb +8 -4
- data/lib/rack/request.rb +222 -63
- data/lib/rack/response.rb +127 -44
- data/lib/rack/rewindable_input.rb +4 -3
- data/lib/rack/runtime.rb +6 -4
- data/lib/rack/sendfile.rb +13 -9
- data/lib/rack/server.rb +95 -24
- data/lib/rack/session/abstract/id.rb +34 -21
- data/lib/rack/session/cookie.rb +12 -12
- data/lib/rack/session/memcache.rb +4 -93
- data/lib/rack/session/pool.rb +5 -3
- data/lib/rack/show_exceptions.rb +21 -17
- data/lib/rack/show_status.rb +9 -9
- data/lib/rack/static.rb +23 -11
- data/lib/rack/tempfile_reaper.rb +1 -1
- data/lib/rack/urlmap.rb +13 -7
- data/lib/rack/utils.rb +105 -111
- data/lib/rack/version.rb +29 -0
- data/lib/rack.rb +67 -73
- data/rack.gemspec +40 -28
- metadata +36 -179
- data/HISTORY.md +0 -520
- 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
@@ -1,10 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
|
2
4
|
# bugrep: Andreas Zehnder
|
3
5
|
|
4
|
-
|
6
|
+
require_relative '../../../rack'
|
5
7
|
require 'time'
|
6
|
-
require 'rack/request'
|
7
|
-
require 'rack/response'
|
8
8
|
require 'securerandom'
|
9
9
|
require 'digest/sha2'
|
10
10
|
|
@@ -84,7 +84,12 @@ module Rack
|
|
84
84
|
@data[key.to_s]
|
85
85
|
end
|
86
86
|
|
87
|
-
def
|
87
|
+
def dig(key, *keys)
|
88
|
+
load_for_read!
|
89
|
+
@data.dig(key.to_s, *keys)
|
90
|
+
end
|
91
|
+
|
92
|
+
def fetch(key, default = Unspecified, &block)
|
88
93
|
load_for_read!
|
89
94
|
if default == Unspecified
|
90
95
|
@data.fetch(key.to_s, &block)
|
@@ -187,8 +192,9 @@ module Rack
|
|
187
192
|
end
|
188
193
|
|
189
194
|
def stringify_keys(other)
|
195
|
+
# Use transform_keys after dropping Ruby 2.4 support
|
190
196
|
hash = {}
|
191
|
-
other.each do |key, value|
|
197
|
+
other.to_hash.each do |key, value|
|
192
198
|
hash[key.to_s] = value
|
193
199
|
end
|
194
200
|
hash
|
@@ -197,8 +203,8 @@ module Rack
|
|
197
203
|
|
198
204
|
# ID sets up a basic framework for implementing an id based sessioning
|
199
205
|
# service. Cookies sent to the client for maintaining sessions will only
|
200
|
-
# contain an id reference. Only #find_session
|
201
|
-
# required to be overwritten.
|
206
|
+
# contain an id reference. Only #find_session, #write_session and
|
207
|
+
# #delete_session are required to be overwritten.
|
202
208
|
#
|
203
209
|
# All parameters are optional.
|
204
210
|
# * :key determines the name of the cookie, by default it is
|
@@ -226,26 +232,27 @@ module Rack
|
|
226
232
|
|
227
233
|
class Persisted
|
228
234
|
DEFAULT_OPTIONS = {
|
229
|
-
:
|
230
|
-
:
|
231
|
-
:
|
232
|
-
:
|
233
|
-
:
|
234
|
-
:
|
235
|
-
:
|
236
|
-
:
|
237
|
-
:
|
238
|
-
:
|
239
|
-
:
|
235
|
+
key: RACK_SESSION,
|
236
|
+
path: '/',
|
237
|
+
domain: nil,
|
238
|
+
expire_after: nil,
|
239
|
+
secure: false,
|
240
|
+
httponly: true,
|
241
|
+
defer: false,
|
242
|
+
renew: false,
|
243
|
+
sidbits: 128,
|
244
|
+
cookie_only: true,
|
245
|
+
secure_random: ::SecureRandom
|
240
246
|
}.freeze
|
241
247
|
|
242
248
|
attr_reader :key, :default_options, :sid_secure
|
243
249
|
|
244
|
-
def initialize(app, options={})
|
250
|
+
def initialize(app, options = {})
|
245
251
|
@app = app
|
246
252
|
@default_options = self.class::DEFAULT_OPTIONS.merge(options)
|
247
253
|
@key = @default_options.delete(:key)
|
248
254
|
@cookie_only = @default_options.delete(:cookie_only)
|
255
|
+
@same_site = @default_options.delete(:same_site)
|
249
256
|
initialize_sid
|
250
257
|
end
|
251
258
|
|
@@ -253,7 +260,7 @@ module Rack
|
|
253
260
|
context(env)
|
254
261
|
end
|
255
262
|
|
256
|
-
def context(env, app
|
263
|
+
def context(env, app = @app)
|
257
264
|
req = make_request env
|
258
265
|
prepare_session(req)
|
259
266
|
status, headers, body = app.call(req.env)
|
@@ -376,7 +383,7 @@ module Rack
|
|
376
383
|
|
377
384
|
session.send(:load!) unless loaded_session?(session)
|
378
385
|
session_id ||= session.id
|
379
|
-
session_data = session.to_hash.delete_if { |k,v| v.nil? }
|
386
|
+
session_data = session.to_hash.delete_if { |k, v| v.nil? }
|
380
387
|
|
381
388
|
if not data = write_session(req, session_id, session_data, options)
|
382
389
|
req.get_header(RACK_ERRORS).puts("Warning! #{self.class.name} failed to save session. Content dropped.")
|
@@ -387,6 +394,12 @@ module Rack
|
|
387
394
|
cookie[:value] = cookie_value(data)
|
388
395
|
cookie[:expires] = Time.now + options[:expire_after] if options[:expire_after]
|
389
396
|
cookie[:expires] = Time.now + options[:max_age] if options[:max_age]
|
397
|
+
|
398
|
+
if @same_site.respond_to? :call
|
399
|
+
cookie[:same_site] = @same_site.call(req, res)
|
400
|
+
else
|
401
|
+
cookie[:same_site] = @same_site
|
402
|
+
end
|
390
403
|
set_cookie(req, res, cookie.merge!(options))
|
391
404
|
end
|
392
405
|
end
|
data/lib/rack/session/cookie.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'openssl'
|
2
4
|
require 'zlib'
|
3
|
-
|
4
|
-
require 'rack/response'
|
5
|
-
require 'rack/session/abstract/id'
|
5
|
+
require_relative 'abstract/id'
|
6
6
|
require 'json'
|
7
|
+
require 'base64'
|
8
|
+
require 'delegate'
|
7
9
|
|
8
10
|
module Rack
|
9
11
|
|
@@ -49,11 +51,11 @@ module Rack
|
|
49
51
|
# Encode session cookies as Base64
|
50
52
|
class Base64
|
51
53
|
def encode(str)
|
52
|
-
|
54
|
+
::Base64.strict_encode64(str)
|
53
55
|
end
|
54
56
|
|
55
57
|
def decode(str)
|
56
|
-
|
58
|
+
::Base64.decode64(str)
|
57
59
|
end
|
58
60
|
|
59
61
|
# Encode session cookies as Marshaled Base64 data
|
@@ -103,7 +105,7 @@ module Rack
|
|
103
105
|
|
104
106
|
attr_reader :coder
|
105
107
|
|
106
|
-
def initialize(app, options={})
|
108
|
+
def initialize(app, options = {})
|
107
109
|
@secrets = options.values_at(:secret, :old_secret).compact
|
108
110
|
@hmac = options.fetch(:hmac, OpenSSL::Digest::SHA1)
|
109
111
|
|
@@ -116,8 +118,8 @@ module Rack
|
|
116
118
|
|
117
119
|
Called from: #{caller[0]}.
|
118
120
|
MSG
|
119
|
-
@coder
|
120
|
-
super(app, options.merge!(:
|
121
|
+
@coder = options[:coder] ||= Base64::Marshal.new
|
122
|
+
super(app, options.merge!(cookie_only: true))
|
121
123
|
end
|
122
124
|
|
123
125
|
private
|
@@ -137,9 +139,7 @@ module Rack
|
|
137
139
|
session_data = request.cookies[@key]
|
138
140
|
|
139
141
|
if @secrets.size > 0 && session_data
|
140
|
-
|
141
|
-
digest.reverse! if digest
|
142
|
-
session_data.reverse! if session_data
|
142
|
+
session_data, _, digest = session_data.rpartition('--')
|
143
143
|
session_data = nil unless digest_match?(session_data, digest)
|
144
144
|
end
|
145
145
|
|
@@ -147,7 +147,7 @@ module Rack
|
|
147
147
|
end
|
148
148
|
end
|
149
149
|
|
150
|
-
def persistent_session_id!(data, sid=nil)
|
150
|
+
def persistent_session_id!(data, sid = nil)
|
151
151
|
data ||= {}
|
152
152
|
data["session_id"] ||= sid || generate_sid
|
153
153
|
data
|
@@ -1,99 +1,10 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'rack/session/
|
4
|
-
require 'memcache'
|
3
|
+
require 'rack/session/dalli'
|
5
4
|
|
6
5
|
module Rack
|
7
6
|
module Session
|
8
|
-
|
9
|
-
|
10
|
-
# maintained in the cookie.
|
11
|
-
# You may treat Session::Memcache as you would Session::Pool with the
|
12
|
-
# following caveats.
|
13
|
-
#
|
14
|
-
# * Setting :expire_after to 0 would note to the Memcache server to hang
|
15
|
-
# onto the session data until it would drop it according to it's own
|
16
|
-
# specifications. However, the cookie sent to the client would expire
|
17
|
-
# immediately.
|
18
|
-
#
|
19
|
-
# Note that memcache does drop data before it may be listed to expire. For
|
20
|
-
# a full description of behaviour, please see memcache's documentation.
|
21
|
-
|
22
|
-
class Memcache < Abstract::PersistedSecure
|
23
|
-
attr_reader :mutex, :pool
|
24
|
-
|
25
|
-
DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge \
|
26
|
-
:namespace => 'rack:session',
|
27
|
-
:memcache_server => 'localhost:11211'
|
28
|
-
|
29
|
-
def initialize(app, options={})
|
30
|
-
super
|
31
|
-
|
32
|
-
@mutex = Mutex.new
|
33
|
-
mserv = @default_options[:memcache_server]
|
34
|
-
mopts = @default_options.reject{|k,v| !MemCache::DEFAULT_OPTIONS.include? k }
|
35
|
-
|
36
|
-
@pool = options[:cache] || MemCache.new(mserv, mopts)
|
37
|
-
unless @pool.active? and @pool.servers.any?(&:alive?)
|
38
|
-
raise 'No memcache servers'
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
def generate_sid
|
43
|
-
loop do
|
44
|
-
sid = super
|
45
|
-
break sid unless @pool.get(sid.private_id, true)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def find_session(req, sid)
|
50
|
-
with_lock(req) do
|
51
|
-
unless sid and session = get_session_with_fallback(sid)
|
52
|
-
sid, session = generate_sid, {}
|
53
|
-
unless /^STORED/ =~ @pool.add(sid.private_id, session)
|
54
|
-
raise "Session collision on '#{sid.inspect}'"
|
55
|
-
end
|
56
|
-
end
|
57
|
-
[sid, session]
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def write_session(req, session_id, new_session, options)
|
62
|
-
expiry = options[:expire_after]
|
63
|
-
expiry = expiry.nil? ? 0 : expiry + 1
|
64
|
-
|
65
|
-
with_lock(req) do
|
66
|
-
@pool.set session_id.private_id, new_session, expiry
|
67
|
-
session_id
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
def delete_session(req, session_id, options)
|
72
|
-
with_lock(req) do
|
73
|
-
@pool.delete(session_id.public_id)
|
74
|
-
@pool.delete(session_id.private_id)
|
75
|
-
generate_sid unless options[:drop]
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
def with_lock(req)
|
80
|
-
@mutex.lock if req.multithread?
|
81
|
-
yield
|
82
|
-
rescue MemCache::MemCacheError, Errno::ECONNREFUSED
|
83
|
-
if $VERBOSE
|
84
|
-
warn "#{self} is unable to find memcached server."
|
85
|
-
warn $!.inspect
|
86
|
-
end
|
87
|
-
raise
|
88
|
-
ensure
|
89
|
-
@mutex.unlock if @mutex.locked?
|
90
|
-
end
|
91
|
-
|
92
|
-
private
|
93
|
-
|
94
|
-
def get_session_with_fallback(sid)
|
95
|
-
@pool.get(sid.private_id) || @pool.get(sid.public_id)
|
96
|
-
end
|
97
|
-
end
|
7
|
+
warn "Rack::Session::Memcache is deprecated, please use Rack::Session::Dalli from 'dalli' gem instead."
|
8
|
+
Memcache = Dalli
|
98
9
|
end
|
99
10
|
end
|
data/lib/rack/session/pool.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
|
2
4
|
# THANKS:
|
3
5
|
# apeiros, for session id generation, expiry setup, and threadiness
|
4
6
|
# sergio, threadiness and bugreps
|
5
7
|
|
6
|
-
|
8
|
+
require_relative 'abstract/id'
|
7
9
|
require 'thread'
|
8
10
|
|
9
11
|
module Rack
|
@@ -26,9 +28,9 @@ module Rack
|
|
26
28
|
|
27
29
|
class Pool < Abstract::PersistedSecure
|
28
30
|
attr_reader :mutex, :pool
|
29
|
-
DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge :
|
31
|
+
DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge drop: false
|
30
32
|
|
31
|
-
def initialize(app, options={})
|
33
|
+
def initialize(app, options = {})
|
32
34
|
super
|
33
35
|
@pool = Hash.new
|
34
36
|
@mutex = Mutex.new
|
data/lib/rack/show_exceptions.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'ostruct'
|
2
4
|
require 'erb'
|
3
|
-
require 'rack/request'
|
4
|
-
require 'rack/utils'
|
5
5
|
|
6
6
|
module Rack
|
7
7
|
# Rack::ShowExceptions catches all exceptions raised from the app it
|
@@ -55,7 +55,7 @@ module Rack
|
|
55
55
|
private :accepts_html?
|
56
56
|
|
57
57
|
def dump_exception(exception)
|
58
|
-
string = "#{exception.class}: #{exception.message}\n"
|
58
|
+
string = "#{exception.class}: #{exception.message}\n".dup
|
59
59
|
string << exception.backtrace.map { |l| "\t#{l}" }.join("\n")
|
60
60
|
string
|
61
61
|
end
|
@@ -63,12 +63,12 @@ module Rack
|
|
63
63
|
def pretty(env, exception)
|
64
64
|
req = Rack::Request.new(env)
|
65
65
|
|
66
|
-
# This double assignment is to prevent an "unused variable" warning
|
67
|
-
#
|
66
|
+
# This double assignment is to prevent an "unused variable" warning.
|
67
|
+
# Yes, it is dumb, but I don't like Ruby yelling at me.
|
68
68
|
path = path = (req.script_name + req.path_info).squeeze("/")
|
69
69
|
|
70
|
-
# This double assignment is to prevent an "unused variable" warning
|
71
|
-
#
|
70
|
+
# This double assignment is to prevent an "unused variable" warning.
|
71
|
+
# Yes, it is dumb, but I don't like Ruby yelling at me.
|
72
72
|
frames = frames = exception.backtrace.map { |line|
|
73
73
|
frame = OpenStruct.new
|
74
74
|
if line =~ /(.*?):(\d+)(:in `(.*)')?/
|
@@ -77,13 +77,13 @@ module Rack
|
|
77
77
|
frame.function = $4
|
78
78
|
|
79
79
|
begin
|
80
|
-
lineno = frame.lineno-1
|
80
|
+
lineno = frame.lineno - 1
|
81
81
|
lines = ::File.readlines(frame.filename)
|
82
|
-
frame.pre_context_lineno = [lineno-CONTEXT, 0].max
|
82
|
+
frame.pre_context_lineno = [lineno - CONTEXT, 0].max
|
83
83
|
frame.pre_context = lines[frame.pre_context_lineno...lineno]
|
84
84
|
frame.context_line = lines[lineno].chomp
|
85
|
-
frame.post_context_lineno = [lineno+CONTEXT, lines.size].min
|
86
|
-
frame.post_context = lines[lineno+1..frame.post_context_lineno]
|
85
|
+
frame.post_context_lineno = [lineno + CONTEXT, lines.size].min
|
86
|
+
frame.post_context = lines[lineno + 1..frame.post_context_lineno]
|
87
87
|
rescue
|
88
88
|
end
|
89
89
|
|
@@ -93,7 +93,11 @@ module Rack
|
|
93
93
|
end
|
94
94
|
}.compact
|
95
95
|
|
96
|
-
|
96
|
+
template.result(binding)
|
97
|
+
end
|
98
|
+
|
99
|
+
def template
|
100
|
+
TEMPLATE
|
97
101
|
end
|
98
102
|
|
99
103
|
def h(obj) # :nodoc:
|
@@ -107,8 +111,8 @@ module Rack
|
|
107
111
|
|
108
112
|
# :stopdoc:
|
109
113
|
|
110
|
-
# adapted from Django <djangoproject.com>
|
111
|
-
# Copyright (c)
|
114
|
+
# adapted from Django <www.djangoproject.com>
|
115
|
+
# Copyright (c) Django Software Foundation and individual contributors.
|
112
116
|
# Used under the modified BSD license:
|
113
117
|
# http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
|
114
118
|
TEMPLATE = ERB.new(<<-'HTML'.gsub(/^ /, ''))
|
@@ -307,7 +311,7 @@ module Rack
|
|
307
311
|
<% end %>
|
308
312
|
|
309
313
|
<h3 id="post-info">POST</h3>
|
310
|
-
<% if req.POST and not req.POST.empty? %>
|
314
|
+
<% if ((req.POST and not req.POST.empty?) rescue (no_post_data = "Invalid POST data"; nil)) %>
|
311
315
|
<table class="req">
|
312
316
|
<thead>
|
313
317
|
<tr>
|
@@ -325,7 +329,7 @@ module Rack
|
|
325
329
|
</tbody>
|
326
330
|
</table>
|
327
331
|
<% else %>
|
328
|
-
<p
|
332
|
+
<p><%= no_post_data || "No POST data" %>.</p>
|
329
333
|
<% end %>
|
330
334
|
|
331
335
|
|
@@ -363,7 +367,7 @@ module Rack
|
|
363
367
|
<% env.sort_by { |k, v| k.to_s }.each { |key, val| %>
|
364
368
|
<tr>
|
365
369
|
<td><%=h key %></td>
|
366
|
-
<td class="code"><div><%=h val %></div></td>
|
370
|
+
<td class="code"><div><%=h val.inspect %></div></td>
|
367
371
|
</tr>
|
368
372
|
<% } %>
|
369
373
|
</tbody>
|
data/lib/rack/show_status.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'erb'
|
2
|
-
require 'rack/request'
|
3
|
-
require 'rack/utils'
|
4
4
|
|
5
5
|
module Rack
|
6
6
|
# Rack::ShowStatus catches all empty responses and replaces them
|
@@ -18,19 +18,19 @@ module Rack
|
|
18
18
|
|
19
19
|
def call(env)
|
20
20
|
status, headers, body = @app.call(env)
|
21
|
-
headers = Utils::HeaderHash
|
21
|
+
headers = Utils::HeaderHash[headers]
|
22
22
|
empty = headers[CONTENT_LENGTH].to_i <= 0
|
23
23
|
|
24
24
|
# client or server error, or explicit message
|
25
25
|
if (status.to_i >= 400 && empty) || env[RACK_SHOWSTATUS_DETAIL]
|
26
|
-
# This double assignment is to prevent an "unused variable" warning
|
27
|
-
#
|
26
|
+
# This double assignment is to prevent an "unused variable" warning.
|
27
|
+
# Yes, it is dumb, but I don't like Ruby yelling at me.
|
28
28
|
req = req = Rack::Request.new(env)
|
29
29
|
|
30
30
|
message = Rack::Utils::HTTP_STATUS_CODES[status.to_i] || status.to_s
|
31
31
|
|
32
|
-
# This double assignment is to prevent an "unused variable" warning
|
33
|
-
#
|
32
|
+
# This double assignment is to prevent an "unused variable" warning.
|
33
|
+
# Yes, it is dumb, but I don't like Ruby yelling at me.
|
34
34
|
detail = detail = env[RACK_SHOWSTATUS_DETAIL] || message
|
35
35
|
|
36
36
|
body = @template.result(binding)
|
@@ -52,8 +52,8 @@ module Rack
|
|
52
52
|
|
53
53
|
# :stopdoc:
|
54
54
|
|
55
|
-
# adapted from Django <djangoproject.com>
|
56
|
-
# Copyright (c)
|
55
|
+
# adapted from Django <www.djangoproject.com>
|
56
|
+
# Copyright (c) Django Software Foundation and individual contributors.
|
57
57
|
# Used under the modified BSD license:
|
58
58
|
# http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
|
59
59
|
TEMPLATE = <<'HTML'
|
data/lib/rack/static.rb
CHANGED
@@ -1,11 +1,10 @@
|
|
1
|
-
|
2
|
-
require "rack/utils"
|
1
|
+
# frozen_string_literal: true
|
3
2
|
|
4
3
|
module Rack
|
5
4
|
|
6
5
|
# The Rack::Static middleware intercepts requests for static files
|
7
6
|
# (javascript files, images, stylesheets, etc) based on the url prefixes or
|
8
|
-
# route mappings passed in the options, and serves them using a Rack::
|
7
|
+
# route mappings passed in the options, and serves them using a Rack::Files
|
9
8
|
# object. This allows a Rack stack to serve both static and dynamic content.
|
10
9
|
#
|
11
10
|
# Examples:
|
@@ -15,6 +14,11 @@ module Rack
|
|
15
14
|
#
|
16
15
|
# use Rack::Static, :urls => ["/media"]
|
17
16
|
#
|
17
|
+
# Same as previous, but instead of returning 404 for missing files under
|
18
|
+
# /media, call the next middleware:
|
19
|
+
#
|
20
|
+
# use Rack::Static, :urls => ["/media"], :cascade => true
|
21
|
+
#
|
18
22
|
# Serve all requests beginning with /css or /images from the folder "public"
|
19
23
|
# in the current directory (ie public/css/* and public/images/*):
|
20
24
|
#
|
@@ -82,24 +86,26 @@ module Rack
|
|
82
86
|
# ]
|
83
87
|
#
|
84
88
|
class Static
|
89
|
+
(require_relative 'core_ext/regexp'; using ::Rack::RegexpExtensions) if RUBY_VERSION < '2.4'
|
85
90
|
|
86
|
-
def initialize(app, options={})
|
91
|
+
def initialize(app, options = {})
|
87
92
|
@app = app
|
88
93
|
@urls = options[:urls] || ["/favicon.ico"]
|
89
94
|
@index = options[:index]
|
90
95
|
@gzip = options[:gzip]
|
96
|
+
@cascade = options[:cascade]
|
91
97
|
root = options[:root] || Dir.pwd
|
92
98
|
|
93
99
|
# HTTP Headers
|
94
100
|
@header_rules = options[:header_rules] || []
|
95
101
|
# Allow for legacy :cache_control option while prioritizing global header_rules setting
|
96
|
-
@header_rules.unshift([:all, {CACHE_CONTROL => options[:cache_control]}]) if options[:cache_control]
|
102
|
+
@header_rules.unshift([:all, { CACHE_CONTROL => options[:cache_control] }]) if options[:cache_control]
|
97
103
|
|
98
|
-
@file_server = Rack::
|
104
|
+
@file_server = Rack::Files.new(root)
|
99
105
|
end
|
100
106
|
|
101
107
|
def add_index_root?(path)
|
102
|
-
@index && path
|
108
|
+
@index && route_file(path) && path.end_with?('/')
|
103
109
|
end
|
104
110
|
|
105
111
|
def overwrite_file_path(path)
|
@@ -120,7 +126,7 @@ module Rack
|
|
120
126
|
if can_serve(path)
|
121
127
|
if overwrite_file_path(path)
|
122
128
|
env[PATH_INFO] = (add_index_root?(path) ? path + @index : @urls[path])
|
123
|
-
elsif @gzip && env['HTTP_ACCEPT_ENCODING']
|
129
|
+
elsif @gzip && env['HTTP_ACCEPT_ENCODING'] && /\bgzip\b/.match?(env['HTTP_ACCEPT_ENCODING'])
|
124
130
|
path = env[PATH_INFO]
|
125
131
|
env[PATH_INFO] += '.gz'
|
126
132
|
response = @file_server.call(env)
|
@@ -128,6 +134,8 @@ module Rack
|
|
128
134
|
|
129
135
|
if response[0] == 404
|
130
136
|
response = nil
|
137
|
+
elsif response[0] == 304
|
138
|
+
# Do nothing, leave headers as is
|
131
139
|
else
|
132
140
|
if mime_type = Mime.mime_type(::File.extname(path), 'text/plain')
|
133
141
|
response[1][CONTENT_TYPE] = mime_type
|
@@ -139,6 +147,10 @@ module Rack
|
|
139
147
|
path = env[PATH_INFO]
|
140
148
|
response ||= @file_server.call(env)
|
141
149
|
|
150
|
+
if @cascade && response[0] == 404
|
151
|
+
return @app.call(env)
|
152
|
+
end
|
153
|
+
|
142
154
|
headers = response[1]
|
143
155
|
applicable_rules(path).each do |rule, new_headers|
|
144
156
|
new_headers.each { |field, content| headers[field] = content }
|
@@ -157,14 +169,14 @@ module Rack
|
|
157
169
|
when :all
|
158
170
|
true
|
159
171
|
when :fonts
|
160
|
-
|
172
|
+
/\.(?:ttf|otf|eot|woff2|woff|svg)\z/.match?(path)
|
161
173
|
when String
|
162
174
|
path = ::Rack::Utils.unescape(path)
|
163
175
|
path.start_with?(rule) || path.start_with?('/' + rule)
|
164
176
|
when Array
|
165
|
-
|
177
|
+
/\.(#{rule.join('|')})\z/.match?(path)
|
166
178
|
when Regexp
|
167
|
-
path
|
179
|
+
rule.match?(path)
|
168
180
|
else
|
169
181
|
false
|
170
182
|
end
|
data/lib/rack/tempfile_reaper.rb
CHANGED
data/lib/rack/urlmap.rb
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
|
1
5
|
module Rack
|
2
6
|
# Rack::URLMap takes a hash mapping urls or paths to apps, and
|
3
7
|
# dispatches accordingly. Support for HTTP/1.1 host names exists if
|
@@ -12,17 +16,16 @@ module Rack
|
|
12
16
|
# first, since they are most specific.
|
13
17
|
|
14
18
|
class URLMap
|
15
|
-
NEGATIVE_INFINITY = -1.0 / 0.0
|
16
|
-
INFINITY = 1.0 / 0.0
|
17
|
-
|
18
19
|
def initialize(map = {})
|
19
20
|
remap(map)
|
20
21
|
end
|
21
22
|
|
22
23
|
def remap(map)
|
24
|
+
@known_hosts = Set[]
|
23
25
|
@mapping = map.map { |location, app|
|
24
26
|
if location =~ %r{\Ahttps?://(.*?)(/.*)}
|
25
27
|
host, location = $1, $2
|
28
|
+
@known_hosts << host
|
26
29
|
else
|
27
30
|
host = nil
|
28
31
|
end
|
@@ -32,11 +35,11 @@ module Rack
|
|
32
35
|
end
|
33
36
|
|
34
37
|
location = location.chomp('/')
|
35
|
-
match = Regexp.new("^#{Regexp.quote(location).gsub('/', '/+')}(.*)",
|
38
|
+
match = Regexp.new("^#{Regexp.quote(location).gsub('/', '/+')}(.*)", Regexp::NOENCODING)
|
36
39
|
|
37
40
|
[host, location, match, app]
|
38
41
|
}.sort_by do |(host, location, _, _)|
|
39
|
-
[host ? -host.size : INFINITY, -location.size]
|
42
|
+
[host ? -host.size : Float::INFINITY, -location.size]
|
40
43
|
end
|
41
44
|
end
|
42
45
|
|
@@ -50,10 +53,13 @@ module Rack
|
|
50
53
|
is_same_server = casecmp?(http_host, server_name) ||
|
51
54
|
casecmp?(http_host, "#{server_name}:#{server_port}")
|
52
55
|
|
56
|
+
is_host_known = @known_hosts.include? http_host
|
57
|
+
|
53
58
|
@mapping.each do |host, location, match, app|
|
54
59
|
unless casecmp?(http_host, host) \
|
55
60
|
|| casecmp?(server_name, host) \
|
56
|
-
|| (!host && is_same_server)
|
61
|
+
|| (!host && is_same_server) \
|
62
|
+
|| (!host && !is_host_known) # If we don't have a matching host, default to the first without a specified host
|
57
63
|
next
|
58
64
|
end
|
59
65
|
|
@@ -68,7 +74,7 @@ module Rack
|
|
68
74
|
return app.call(env)
|
69
75
|
end
|
70
76
|
|
71
|
-
[404, {CONTENT_TYPE => "text/plain", "X-Cascade" => "pass"}, ["Not Found: #{path}"]]
|
77
|
+
[404, { CONTENT_TYPE => "text/plain", "X-Cascade" => "pass" }, ["Not Found: #{path}"]]
|
72
78
|
|
73
79
|
ensure
|
74
80
|
env[PATH_INFO] = path
|