rack 2.0.9.3 → 2.2.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 +675 -0
- data/CONTRIBUTING.md +136 -0
- data/{COPYING → MIT-LICENSE} +4 -2
- data/README.rdoc +152 -162
- 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 -28
- 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 +5 -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 +17 -11
- 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 +72 -26
- 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 +4 -2
- 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 +58 -73
- data/lib/rack/multipart/uploaded_file.rb +15 -7
- data/lib/rack/multipart.rb +7 -4
- data/lib/rack/null_logger.rb +2 -0
- data/lib/rack/query_parser.rb +53 -28
- data/lib/rack/recursive.rb +7 -5
- data/lib/rack/reloader.rb +8 -4
- data/lib/rack/request.rb +210 -61
- 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 +33 -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 +17 -13
- data/lib/rack/show_status.rb +5 -5
- data/lib/rack/static.rb +23 -11
- data/lib/rack/tempfile_reaper.rb +1 -1
- data/lib/rack/urlmap.rb +12 -6
- data/lib/rack/utils.rb +105 -130
- data/lib/rack/version.rb +29 -0
- data/lib/rack.rb +67 -73
- data/rack.gemspec +40 -28
- metadata +39 -182
- 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,6 +1,7 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
2
4
|
require 'tempfile'
|
3
|
-
require 'rack/utils'
|
4
5
|
|
5
6
|
module Rack
|
6
7
|
# Class which can make any IO object rewindable, including non-rewindable ones. It does
|
@@ -40,7 +41,7 @@ module Rack
|
|
40
41
|
end
|
41
42
|
|
42
43
|
# Closes this RewindableInput object without closing the originally
|
43
|
-
# wrapped IO
|
44
|
+
# wrapped IO object. Cleans up any temporary resources that this RewindableInput
|
44
45
|
# has created.
|
45
46
|
#
|
46
47
|
# This method may be called multiple times. It does nothing on subsequent calls.
|
@@ -72,7 +73,7 @@ module Rack
|
|
72
73
|
@unlinked = true
|
73
74
|
end
|
74
75
|
|
75
|
-
buffer = ""
|
76
|
+
buffer = "".dup
|
76
77
|
while @io.read(1024 * 4, buffer)
|
77
78
|
entire_buffer_written_out = false
|
78
79
|
while !entire_buffer_written_out
|
data/lib/rack/runtime.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Rack
|
4
4
|
# Sets an "X-Runtime" response header, indicating the response
|
@@ -8,8 +8,8 @@ module Rack
|
|
8
8
|
# time, or before all the other middlewares to include time for them,
|
9
9
|
# too.
|
10
10
|
class Runtime
|
11
|
-
FORMAT_STRING = "%0.6f"
|
12
|
-
HEADER_NAME = "X-Runtime"
|
11
|
+
FORMAT_STRING = "%0.6f" # :nodoc:
|
12
|
+
HEADER_NAME = "X-Runtime" # :nodoc:
|
13
13
|
|
14
14
|
def initialize(app, name = nil)
|
15
15
|
@app = app
|
@@ -20,9 +20,11 @@ module Rack
|
|
20
20
|
def call(env)
|
21
21
|
start_time = Utils.clock_time
|
22
22
|
status, headers, body = @app.call(env)
|
23
|
+
headers = Utils::HeaderHash[headers]
|
24
|
+
|
23
25
|
request_time = Utils.clock_time - start_time
|
24
26
|
|
25
|
-
unless headers.
|
27
|
+
unless headers.key?(@header_name)
|
26
28
|
headers[@header_name] = FORMAT_STRING % request_time
|
27
29
|
end
|
28
30
|
|
data/lib/rack/sendfile.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
|
2
|
-
require 'rack/body_proxy'
|
1
|
+
# frozen_string_literal: true
|
3
2
|
|
4
3
|
module Rack
|
5
4
|
|
@@ -14,7 +13,7 @@ module Rack
|
|
14
13
|
#
|
15
14
|
# In order to take advantage of this middleware, the response body must
|
16
15
|
# respond to +to_path+ and the request must include an X-Sendfile-Type
|
17
|
-
# header. Rack::
|
16
|
+
# header. Rack::Files and other components implement +to_path+ so there's
|
18
17
|
# rarely anything you need to do in your application. The X-Sendfile-Type
|
19
18
|
# header is typically set in your web servers configuration. The following
|
20
19
|
# sections attempt to document
|
@@ -53,7 +52,7 @@ module Rack
|
|
53
52
|
# that it maps to. The middleware performs a simple substitution on the
|
54
53
|
# resulting path.
|
55
54
|
#
|
56
|
-
# See Also:
|
55
|
+
# See Also: https://www.nginx.com/resources/wiki/start/topics/examples/xsendfile
|
57
56
|
#
|
58
57
|
# === lighttpd
|
59
58
|
#
|
@@ -99,7 +98,7 @@ module Rack
|
|
99
98
|
# will be matched with case indifference.
|
100
99
|
|
101
100
|
class Sendfile
|
102
|
-
def initialize(app, variation=nil, mappings=[])
|
101
|
+
def initialize(app, variation = nil, mappings = [])
|
103
102
|
@app = app
|
104
103
|
@variation = variation
|
105
104
|
@mappings = mappings.map do |internal, external|
|
@@ -115,7 +114,8 @@ module Rack
|
|
115
114
|
path = ::File.expand_path(body.to_path)
|
116
115
|
if url = map_accel_path(env, path)
|
117
116
|
headers[CONTENT_LENGTH] = '0'
|
118
|
-
|
117
|
+
# '?' must be percent-encoded because it is not query string but a part of path
|
118
|
+
headers[type] = ::Rack::Utils.escape_path(url).gsub('?', '%3F')
|
119
119
|
obody = body
|
120
120
|
body = Rack::BodyProxy.new([]) do
|
121
121
|
obody.close if obody.respond_to?(:close)
|
@@ -147,11 +147,15 @@ module Rack
|
|
147
147
|
end
|
148
148
|
|
149
149
|
def map_accel_path(env, path)
|
150
|
-
if mapping = @mappings.find { |internal,_| internal =~ path }
|
150
|
+
if mapping = @mappings.find { |internal, _| internal =~ path }
|
151
151
|
path.sub(*mapping)
|
152
152
|
elsif mapping = env['HTTP_X_ACCEL_MAPPING']
|
153
|
-
|
154
|
-
|
153
|
+
mapping.split(',').map(&:strip).each do |m|
|
154
|
+
internal, external = m.split('=', 2).map(&:strip)
|
155
|
+
new_path = path.sub(/^#{internal}/i, external)
|
156
|
+
return new_path unless path == new_path
|
157
|
+
end
|
158
|
+
path
|
155
159
|
end
|
156
160
|
end
|
157
161
|
end
|
data/lib/rack/server.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'optparse'
|
2
4
|
require 'fileutils'
|
3
5
|
|
4
|
-
|
5
6
|
module Rack
|
6
7
|
|
7
8
|
class Server
|
9
|
+
(require_relative 'core_ext/regexp'; using ::Rack::RegexpExtensions) if RUBY_VERSION < '2.4'
|
8
10
|
|
9
11
|
class Options
|
10
12
|
def parse!(args)
|
@@ -21,10 +23,6 @@ module Rack
|
|
21
23
|
lineno += 1
|
22
24
|
}
|
23
25
|
|
24
|
-
opts.on("-b", "--builder BUILDER_LINE", "evaluate a BUILDER_LINE of code as a builder script") { |line|
|
25
|
-
options[:builder] = line
|
26
|
-
}
|
27
|
-
|
28
26
|
opts.on("-d", "--debug", "set debugging flags (set $DEBUG to true)") {
|
29
27
|
options[:debug] = true
|
30
28
|
}
|
@@ -42,12 +40,16 @@ module Rack
|
|
42
40
|
|
43
41
|
opts.on("-r", "--require LIBRARY",
|
44
42
|
"require the library, before executing your script") { |library|
|
45
|
-
options[:require]
|
43
|
+
(options[:require] ||= []) << library
|
46
44
|
}
|
47
45
|
|
48
46
|
opts.separator ""
|
49
47
|
opts.separator "Rack options:"
|
50
|
-
opts.on("-
|
48
|
+
opts.on("-b", "--builder BUILDER_LINE", "evaluate a BUILDER_LINE of code as a builder script") { |line|
|
49
|
+
options[:builder] = line
|
50
|
+
}
|
51
|
+
|
52
|
+
opts.on("-s", "--server SERVER", "serve using SERVER (thin/puma/webrick)") { |s|
|
51
53
|
options[:server] = s
|
52
54
|
}
|
53
55
|
|
@@ -77,6 +79,24 @@ module Rack
|
|
77
79
|
options[:pid] = ::File.expand_path(f)
|
78
80
|
}
|
79
81
|
|
82
|
+
opts.separator ""
|
83
|
+
opts.separator "Profiling options:"
|
84
|
+
|
85
|
+
opts.on("--heap HEAPFILE", "Build the application, then dump the heap to HEAPFILE") do |e|
|
86
|
+
options[:heapfile] = e
|
87
|
+
end
|
88
|
+
|
89
|
+
opts.on("--profile PROFILE", "Dump CPU or Memory profile to PROFILE (defaults to a tempfile)") do |e|
|
90
|
+
options[:profile_file] = e
|
91
|
+
end
|
92
|
+
|
93
|
+
opts.on("--profile-mode MODE", "Profile mode (cpu|wall|object)") do |e|
|
94
|
+
{ cpu: true, wall: true, object: true }.fetch(e.to_sym) do
|
95
|
+
raise OptionParser::InvalidOption, "unknown profile mode: #{e}"
|
96
|
+
end
|
97
|
+
options[:profile_mode] = e.to_sym
|
98
|
+
end
|
99
|
+
|
80
100
|
opts.separator ""
|
81
101
|
opts.separator "Common options:"
|
82
102
|
|
@@ -114,14 +134,14 @@ module Rack
|
|
114
134
|
|
115
135
|
has_options = false
|
116
136
|
server.valid_options.each do |name, description|
|
117
|
-
next if
|
137
|
+
next if /^(Host|Port)[^a-zA-Z]/.match?(name.to_s) # ignore handler's host and port options, we do our own.
|
118
138
|
info << " -O %-21s %s" % [name, description]
|
119
139
|
has_options = true
|
120
140
|
end
|
121
141
|
return "" if !has_options
|
122
142
|
end
|
123
143
|
info.join("\n")
|
124
|
-
rescue NameError
|
144
|
+
rescue NameError, LoadError
|
125
145
|
return "Warning: Could not find handler specified (#{options[:server] || 'default'}) to determine handler-specific options"
|
126
146
|
end
|
127
147
|
end
|
@@ -152,7 +172,9 @@ module Rack
|
|
152
172
|
|
153
173
|
# Options may include:
|
154
174
|
# * :app
|
155
|
-
# a rack application to run (overrides :config)
|
175
|
+
# a rack application to run (overrides :config and :builder)
|
176
|
+
# * :builder
|
177
|
+
# a string to evaluate a Rack::Builder from
|
156
178
|
# * :config
|
157
179
|
# a rackup configuration file path to load (.ru)
|
158
180
|
# * :environment
|
@@ -182,6 +204,14 @@ module Rack
|
|
182
204
|
# add given paths to $LOAD_PATH
|
183
205
|
# * :require
|
184
206
|
# require the given libraries
|
207
|
+
#
|
208
|
+
# Additional options for profiling app initialization include:
|
209
|
+
# * :heapfile
|
210
|
+
# location for ObjectSpace.dump_all to write the output to
|
211
|
+
# * :profile_file
|
212
|
+
# location for CPU/Memory (StackProf) profile output (defaults to a tempfile)
|
213
|
+
# * :profile_mode
|
214
|
+
# StackProf profile mode (cpu|wall|object)
|
185
215
|
def initialize(options = nil)
|
186
216
|
@ignore_options = []
|
187
217
|
|
@@ -206,12 +236,12 @@ module Rack
|
|
206
236
|
default_host = environment == 'development' ? 'localhost' : '0.0.0.0'
|
207
237
|
|
208
238
|
{
|
209
|
-
:
|
210
|
-
:
|
211
|
-
:
|
212
|
-
:
|
213
|
-
:
|
214
|
-
:
|
239
|
+
environment: environment,
|
240
|
+
pid: nil,
|
241
|
+
Port: 9292,
|
242
|
+
Host: default_host,
|
243
|
+
AccessLog: [],
|
244
|
+
config: "config.ru"
|
215
245
|
}
|
216
246
|
end
|
217
247
|
|
@@ -222,21 +252,19 @@ module Rack
|
|
222
252
|
class << self
|
223
253
|
def logging_middleware
|
224
254
|
lambda { |server|
|
225
|
-
server.server.name
|
255
|
+
/CGI/.match?(server.server.name) || server.options[:quiet] ? nil : [Rack::CommonLogger, $stderr]
|
226
256
|
}
|
227
257
|
end
|
228
258
|
|
229
259
|
def default_middleware_by_environment
|
230
|
-
m = Hash.new {|h,k| h[k] = []}
|
260
|
+
m = Hash.new {|h, k| h[k] = []}
|
231
261
|
m["deployment"] = [
|
232
262
|
[Rack::ContentLength],
|
233
|
-
[Rack::Chunked],
|
234
263
|
logging_middleware,
|
235
264
|
[Rack::TempfileReaper]
|
236
265
|
]
|
237
266
|
m["development"] = [
|
238
267
|
[Rack::ContentLength],
|
239
|
-
[Rack::Chunked],
|
240
268
|
logging_middleware,
|
241
269
|
[Rack::ShowExceptions],
|
242
270
|
[Rack::Lint],
|
@@ -255,7 +283,7 @@ module Rack
|
|
255
283
|
self.class.middleware
|
256
284
|
end
|
257
285
|
|
258
|
-
def start
|
286
|
+
def start(&block)
|
259
287
|
if options[:warn]
|
260
288
|
$-w = true
|
261
289
|
end
|
@@ -264,7 +292,7 @@ module Rack
|
|
264
292
|
$LOAD_PATH.unshift(*includes)
|
265
293
|
end
|
266
294
|
|
267
|
-
|
295
|
+
Array(options[:require]).each do |library|
|
268
296
|
require library
|
269
297
|
end
|
270
298
|
|
@@ -280,7 +308,9 @@ module Rack
|
|
280
308
|
|
281
309
|
# Touch the wrapped app, so that the config.ru is loaded before
|
282
310
|
# daemonization (i.e. before chdir, etc).
|
283
|
-
|
311
|
+
handle_profiling(options[:heapfile], options[:profile_mode], options[:profile_file]) do
|
312
|
+
wrapped_app
|
313
|
+
end
|
284
314
|
|
285
315
|
daemonize_app if options[:daemonize]
|
286
316
|
|
@@ -294,7 +324,7 @@ module Rack
|
|
294
324
|
end
|
295
325
|
end
|
296
326
|
|
297
|
-
server.run
|
327
|
+
server.run(wrapped_app, **options, &block)
|
298
328
|
end
|
299
329
|
|
300
330
|
def server
|
@@ -321,6 +351,44 @@ module Rack
|
|
321
351
|
app
|
322
352
|
end
|
323
353
|
|
354
|
+
def handle_profiling(heapfile, profile_mode, filename)
|
355
|
+
if heapfile
|
356
|
+
require "objspace"
|
357
|
+
ObjectSpace.trace_object_allocations_start
|
358
|
+
yield
|
359
|
+
GC.start
|
360
|
+
::File.open(heapfile, "w") { |f| ObjectSpace.dump_all(output: f) }
|
361
|
+
exit
|
362
|
+
end
|
363
|
+
|
364
|
+
if profile_mode
|
365
|
+
require "stackprof"
|
366
|
+
require "tempfile"
|
367
|
+
|
368
|
+
make_profile_name(filename) do |filename|
|
369
|
+
::File.open(filename, "w") do |f|
|
370
|
+
StackProf.run(mode: profile_mode, out: f) do
|
371
|
+
yield
|
372
|
+
end
|
373
|
+
puts "Profile written to: #{filename}"
|
374
|
+
end
|
375
|
+
end
|
376
|
+
exit
|
377
|
+
end
|
378
|
+
|
379
|
+
yield
|
380
|
+
end
|
381
|
+
|
382
|
+
def make_profile_name(filename)
|
383
|
+
if filename
|
384
|
+
yield filename
|
385
|
+
else
|
386
|
+
::Dir::Tmpname.create("profile.dump") do |tmpname, _, _|
|
387
|
+
yield tmpname
|
388
|
+
end
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
324
392
|
def build_app_from_string
|
325
393
|
Rack::Builder.new_from_string(self.options[:builder])
|
326
394
|
end
|
@@ -355,7 +423,10 @@ module Rack
|
|
355
423
|
end
|
356
424
|
|
357
425
|
def daemonize_app
|
426
|
+
# Cannot be covered as it forks
|
427
|
+
# :nocov:
|
358
428
|
Process.daemon
|
429
|
+
# :nocov:
|
359
430
|
end
|
360
431
|
|
361
432
|
def write_pid
|
@@ -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,22 +232,22 @@ 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)
|
@@ -253,7 +259,7 @@ module Rack
|
|
253
259
|
context(env)
|
254
260
|
end
|
255
261
|
|
256
|
-
def context(env, app
|
262
|
+
def context(env, app = @app)
|
257
263
|
req = make_request env
|
258
264
|
prepare_session(req)
|
259
265
|
status, headers, body = app.call(req.env)
|
@@ -376,7 +382,7 @@ module Rack
|
|
376
382
|
|
377
383
|
session.send(:load!) unless loaded_session?(session)
|
378
384
|
session_id ||= session.id
|
379
|
-
session_data = session.to_hash.delete_if { |k,v| v.nil? }
|
385
|
+
session_data = session.to_hash.delete_if { |k, v| v.nil? }
|
380
386
|
|
381
387
|
if not data = write_session(req, session_id, session_data, options)
|
382
388
|
req.get_header(RACK_ERRORS).puts("Warning! #{self.class.name} failed to save session. Content dropped.")
|
@@ -387,6 +393,12 @@ module Rack
|
|
387
393
|
cookie[:value] = cookie_value(data)
|
388
394
|
cookie[:expires] = Time.now + options[:expire_after] if options[:expire_after]
|
389
395
|
cookie[:expires] = Time.now + options[:max_age] if options[:max_age]
|
396
|
+
|
397
|
+
if @same_site.respond_to? :call
|
398
|
+
cookie[:same_site] = @same_site.call(req, res)
|
399
|
+
else
|
400
|
+
cookie[:same_site] = @same_site
|
401
|
+
end
|
390
402
|
set_cookie(req, res, cookie.merge!(options))
|
391
403
|
end
|
392
404
|
end
|
data/lib/rack/session/cookie.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
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'
|
7
8
|
|
8
9
|
module Rack
|
9
10
|
|
@@ -49,11 +50,11 @@ module Rack
|
|
49
50
|
# Encode session cookies as Base64
|
50
51
|
class Base64
|
51
52
|
def encode(str)
|
52
|
-
|
53
|
+
::Base64.strict_encode64(str)
|
53
54
|
end
|
54
55
|
|
55
56
|
def decode(str)
|
56
|
-
|
57
|
+
::Base64.decode64(str)
|
57
58
|
end
|
58
59
|
|
59
60
|
# Encode session cookies as Marshaled Base64 data
|
@@ -103,7 +104,7 @@ module Rack
|
|
103
104
|
|
104
105
|
attr_reader :coder
|
105
106
|
|
106
|
-
def initialize(app, options={})
|
107
|
+
def initialize(app, options = {})
|
107
108
|
@secrets = options.values_at(:secret, :old_secret).compact
|
108
109
|
@hmac = options.fetch(:hmac, OpenSSL::Digest::SHA1)
|
109
110
|
|
@@ -116,8 +117,9 @@ module Rack
|
|
116
117
|
|
117
118
|
Called from: #{caller[0]}.
|
118
119
|
MSG
|
119
|
-
@coder
|
120
|
-
|
120
|
+
@coder = options[:coder] ||= Base64::Marshal.new
|
121
|
+
@same_site = options.delete :same_site
|
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
|