rack 2.0.4 → 2.1.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/{HISTORY.md → CHANGELOG.md} +220 -155
- data/{COPYING → MIT-LICENSE} +4 -2
- data/README.rdoc +77 -119
- data/Rakefile +25 -18
- data/SPEC +3 -4
- 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.rb +63 -60
- data/lib/rack/auth/abstract/handler.rb +3 -1
- data/lib/rack/auth/abstract/request.rb +2 -0
- data/lib/rack/auth/basic.rb +4 -1
- data/lib/rack/auth/digest/md5.rb +9 -7
- 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 +2 -0
- data/lib/rack/body_proxy.rb +3 -6
- data/lib/rack/builder.rb +38 -15
- data/lib/rack/cascade.rb +6 -5
- data/lib/rack/chunked.rb +29 -6
- data/lib/rack/common_logger.rb +9 -8
- data/lib/rack/conditional_get.rb +3 -1
- data/lib/rack/config.rb +2 -0
- data/lib/rack/content_length.rb +3 -1
- data/lib/rack/content_type.rb +3 -1
- data/lib/rack/core_ext/regexp.rb +14 -0
- data/lib/rack/deflater.rb +28 -17
- data/lib/rack/directory.rb +17 -14
- data/lib/rack/etag.rb +3 -1
- data/lib/rack/events.rb +5 -3
- data/lib/rack/file.rb +5 -173
- data/lib/rack/files.rb +178 -0
- data/lib/rack/handler.rb +7 -2
- data/lib/rack/handler/cgi.rb +3 -1
- data/lib/rack/handler/fastcgi.rb +4 -2
- data/lib/rack/handler/lsws.rb +3 -1
- data/lib/rack/handler/scgi.rb +9 -6
- data/lib/rack/handler/thin.rb +3 -1
- data/lib/rack/handler/webrick.rb +4 -2
- data/lib/rack/head.rb +2 -0
- data/lib/rack/lint.rb +14 -11
- data/lib/rack/lobster.rb +7 -5
- data/lib/rack/lock.rb +2 -0
- data/lib/rack/logger.rb +2 -0
- data/lib/rack/media_type.rb +10 -5
- data/lib/rack/method_override.rb +9 -3
- data/lib/rack/mime.rb +9 -1
- data/lib/rack/mock.rb +74 -15
- data/lib/rack/multipart.rb +5 -3
- data/lib/rack/multipart/generator.rb +6 -7
- data/lib/rack/multipart/parser.rb +54 -51
- data/lib/rack/multipart/uploaded_file.rb +2 -0
- data/lib/rack/null_logger.rb +2 -0
- data/lib/rack/query_parser.rb +51 -25
- data/lib/rack/recursive.rb +7 -5
- data/lib/rack/reloader.rb +10 -4
- data/lib/rack/request.rb +89 -23
- data/lib/rack/response.rb +71 -31
- data/lib/rack/rewindable_input.rb +4 -2
- data/lib/rack/runtime.rb +4 -2
- data/lib/rack/sendfile.rb +15 -8
- data/lib/rack/server.rb +88 -16
- data/lib/rack/session/abstract/id.rb +104 -21
- data/lib/rack/session/cookie.rb +21 -11
- data/lib/rack/session/memcache.rb +4 -87
- data/lib/rack/session/pool.rb +17 -8
- data/lib/rack/show_exceptions.rb +16 -10
- data/lib/rack/show_status.rb +4 -2
- data/lib/rack/static.rb +15 -10
- data/lib/rack/tempfile_reaper.rb +2 -0
- data/lib/rack/urlmap.rb +11 -2
- data/lib/rack/utils.rb +55 -70
- data/rack.gemspec +19 -9
- metadata +32 -173
- 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 -95
- 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 -515
- 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 -96
- data/test/spec_mime.rb +0 -51
- data/test/spec_mock.rb +0 -359
- data/test/spec_multipart.rb +0 -722
- data/test/spec_null_logger.rb +0 -21
- data/test/spec_recursive.rb +0 -75
- data/test/spec_request.rb +0 -1393
- data/test/spec_response.rb +0 -510
- 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 -320
- data/test/spec_session_pool.rb +0 -210
- data/test/spec_show_exceptions.rb +0 -80
- 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,4 +1,6 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
2
4
|
require 'tempfile'
|
3
5
|
require 'rack/utils'
|
4
6
|
|
@@ -40,7 +42,7 @@ module Rack
|
|
40
42
|
end
|
41
43
|
|
42
44
|
# Closes this RewindableInput object without closing the originally
|
43
|
-
# wrapped IO
|
45
|
+
# wrapped IO object. Cleans up any temporary resources that this RewindableInput
|
44
46
|
# has created.
|
45
47
|
#
|
46
48
|
# This method may be called multiple times. It does nothing on subsequent calls.
|
@@ -72,7 +74,7 @@ module Rack
|
|
72
74
|
@unlinked = true
|
73
75
|
end
|
74
76
|
|
75
|
-
buffer = ""
|
77
|
+
buffer = "".dup
|
76
78
|
while @io.read(1024 * 4, buffer)
|
77
79
|
entire_buffer_written_out = false
|
78
80
|
while !entire_buffer_written_out
|
data/lib/rack/runtime.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'rack/utils'
|
2
4
|
|
3
5
|
module Rack
|
@@ -8,8 +10,8 @@ module Rack
|
|
8
10
|
# time, or before all the other middlewares to include time for them,
|
9
11
|
# too.
|
10
12
|
class Runtime
|
11
|
-
FORMAT_STRING = "%0.6f"
|
12
|
-
HEADER_NAME = "X-Runtime"
|
13
|
+
FORMAT_STRING = "%0.6f" # :nodoc:
|
14
|
+
HEADER_NAME = "X-Runtime" # :nodoc:
|
13
15
|
|
14
16
|
def initialize(app, name = nil)
|
15
17
|
@app = app
|
data/lib/rack/sendfile.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rack/files'
|
2
4
|
require 'rack/body_proxy'
|
3
5
|
|
4
6
|
module Rack
|
@@ -14,7 +16,7 @@ module Rack
|
|
14
16
|
#
|
15
17
|
# In order to take advantage of this middleware, the response body must
|
16
18
|
# respond to +to_path+ and the request must include an X-Sendfile-Type
|
17
|
-
# header. Rack::
|
19
|
+
# header. Rack::Files and other components implement +to_path+ so there's
|
18
20
|
# rarely anything you need to do in your application. The X-Sendfile-Type
|
19
21
|
# header is typically set in your web servers configuration. The following
|
20
22
|
# sections attempt to document
|
@@ -53,7 +55,7 @@ module Rack
|
|
53
55
|
# that it maps to. The middleware performs a simple substitution on the
|
54
56
|
# resulting path.
|
55
57
|
#
|
56
|
-
# See Also:
|
58
|
+
# See Also: https://www.nginx.com/resources/wiki/start/topics/examples/xsendfile
|
57
59
|
#
|
58
60
|
# === lighttpd
|
59
61
|
#
|
@@ -99,7 +101,7 @@ module Rack
|
|
99
101
|
# will be matched with case indifference.
|
100
102
|
|
101
103
|
class Sendfile
|
102
|
-
def initialize(app, variation=nil, mappings=[])
|
104
|
+
def initialize(app, variation = nil, mappings = [])
|
103
105
|
@app = app
|
104
106
|
@variation = variation
|
105
107
|
@mappings = mappings.map do |internal, external|
|
@@ -115,7 +117,8 @@ module Rack
|
|
115
117
|
path = ::File.expand_path(body.to_path)
|
116
118
|
if url = map_accel_path(env, path)
|
117
119
|
headers[CONTENT_LENGTH] = '0'
|
118
|
-
|
120
|
+
# '?' must be percent-encoded because it is not query string but a part of path
|
121
|
+
headers[type] = ::Rack::Utils.escape_path(url).gsub('?', '%3F')
|
119
122
|
obody = body
|
120
123
|
body = Rack::BodyProxy.new([]) do
|
121
124
|
obody.close if obody.respond_to?(:close)
|
@@ -147,11 +150,15 @@ module Rack
|
|
147
150
|
end
|
148
151
|
|
149
152
|
def map_accel_path(env, path)
|
150
|
-
if mapping = @mappings.find { |internal,_| internal =~ path }
|
153
|
+
if mapping = @mappings.find { |internal, _| internal =~ path }
|
151
154
|
path.sub(*mapping)
|
152
155
|
elsif mapping = env['HTTP_X_ACCEL_MAPPING']
|
153
|
-
|
154
|
-
|
156
|
+
mapping.split(',').map(&:strip).each do |m|
|
157
|
+
internal, external = m.split('=', 2).map(&:strip)
|
158
|
+
new_path = path.sub(/^#{internal}/i, external)
|
159
|
+
return new_path unless path == new_path
|
160
|
+
end
|
161
|
+
path
|
155
162
|
end
|
156
163
|
end
|
157
164
|
end
|
data/lib/rack/server.rb
CHANGED
@@ -1,10 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'optparse'
|
2
4
|
require 'fileutils'
|
3
5
|
|
6
|
+
require_relative 'core_ext/regexp'
|
4
7
|
|
5
8
|
module Rack
|
6
9
|
|
7
10
|
class Server
|
11
|
+
using ::Rack::RegexpExtensions
|
8
12
|
|
9
13
|
class Options
|
10
14
|
def parse!(args)
|
@@ -21,10 +25,6 @@ module Rack
|
|
21
25
|
lineno += 1
|
22
26
|
}
|
23
27
|
|
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
28
|
opts.on("-d", "--debug", "set debugging flags (set $DEBUG to true)") {
|
29
29
|
options[:debug] = true
|
30
30
|
}
|
@@ -47,7 +47,11 @@ module Rack
|
|
47
47
|
|
48
48
|
opts.separator ""
|
49
49
|
opts.separator "Rack options:"
|
50
|
-
opts.on("-
|
50
|
+
opts.on("-b", "--builder BUILDER_LINE", "evaluate a BUILDER_LINE of code as a builder script") { |line|
|
51
|
+
options[:builder] = line
|
52
|
+
}
|
53
|
+
|
54
|
+
opts.on("-s", "--server SERVER", "serve using SERVER (thin/puma/webrick)") { |s|
|
51
55
|
options[:server] = s
|
52
56
|
}
|
53
57
|
|
@@ -77,6 +81,24 @@ module Rack
|
|
77
81
|
options[:pid] = ::File.expand_path(f)
|
78
82
|
}
|
79
83
|
|
84
|
+
opts.separator ""
|
85
|
+
opts.separator "Profiling options:"
|
86
|
+
|
87
|
+
opts.on("--heap HEAPFILE", "Build the application, then dump the heap to HEAPFILE") do |e|
|
88
|
+
options[:heapfile] = e
|
89
|
+
end
|
90
|
+
|
91
|
+
opts.on("--profile PROFILE", "Dump CPU or Memory profile to PROFILE (defaults to a tempfile)") do |e|
|
92
|
+
options[:profile_file] = e
|
93
|
+
end
|
94
|
+
|
95
|
+
opts.on("--profile-mode MODE", "Profile mode (cpu|wall|object)") do |e|
|
96
|
+
{ cpu: true, wall: true, object: true }.fetch(e.to_sym) do
|
97
|
+
raise OptionParser::InvalidOption, "unknown profile mode: #{e}"
|
98
|
+
end
|
99
|
+
options[:profile_mode] = e.to_sym
|
100
|
+
end
|
101
|
+
|
80
102
|
opts.separator ""
|
81
103
|
opts.separator "Common options:"
|
82
104
|
|
@@ -114,7 +136,7 @@ module Rack
|
|
114
136
|
|
115
137
|
has_options = false
|
116
138
|
server.valid_options.each do |name, description|
|
117
|
-
next if
|
139
|
+
next if /^(Host|Port)[^a-zA-Z]/.match?(name.to_s) # ignore handler's host and port options, we do our own.
|
118
140
|
info << " -O %-21s %s" % [name, description]
|
119
141
|
has_options = true
|
120
142
|
end
|
@@ -152,7 +174,9 @@ module Rack
|
|
152
174
|
|
153
175
|
# Options may include:
|
154
176
|
# * :app
|
155
|
-
# a rack application to run (overrides :config)
|
177
|
+
# a rack application to run (overrides :config and :builder)
|
178
|
+
# * :builder
|
179
|
+
# a string to evaluate a Rack::Builder from
|
156
180
|
# * :config
|
157
181
|
# a rackup configuration file path to load (.ru)
|
158
182
|
# * :environment
|
@@ -182,6 +206,14 @@ module Rack
|
|
182
206
|
# add given paths to $LOAD_PATH
|
183
207
|
# * :require
|
184
208
|
# require the given libraries
|
209
|
+
#
|
210
|
+
# Additional options for profiling app initialization include:
|
211
|
+
# * :heapfile
|
212
|
+
# location for ObjectSpace.dump_all to write the output to
|
213
|
+
# * :profile_file
|
214
|
+
# location for CPU/Memory (StackProf) profile output (defaults to a tempfile)
|
215
|
+
# * :profile_mode
|
216
|
+
# StackProf profile mode (cpu|wall|object)
|
185
217
|
def initialize(options = nil)
|
186
218
|
@ignore_options = []
|
187
219
|
|
@@ -206,12 +238,12 @@ module Rack
|
|
206
238
|
default_host = environment == 'development' ? 'localhost' : '0.0.0.0'
|
207
239
|
|
208
240
|
{
|
209
|
-
:
|
210
|
-
:
|
211
|
-
:
|
212
|
-
:
|
213
|
-
:
|
214
|
-
:
|
241
|
+
environment: environment,
|
242
|
+
pid: nil,
|
243
|
+
Port: 9292,
|
244
|
+
Host: default_host,
|
245
|
+
AccessLog: [],
|
246
|
+
config: "config.ru"
|
215
247
|
}
|
216
248
|
end
|
217
249
|
|
@@ -222,12 +254,12 @@ module Rack
|
|
222
254
|
class << self
|
223
255
|
def logging_middleware
|
224
256
|
lambda { |server|
|
225
|
-
server.server.name
|
257
|
+
/CGI/.match?(server.server.name) || server.options[:quiet] ? nil : [Rack::CommonLogger, $stderr]
|
226
258
|
}
|
227
259
|
end
|
228
260
|
|
229
261
|
def default_middleware_by_environment
|
230
|
-
m = Hash.new {|h,k| h[k] = []}
|
262
|
+
m = Hash.new {|h, k| h[k] = []}
|
231
263
|
m["deployment"] = [
|
232
264
|
[Rack::ContentLength],
|
233
265
|
[Rack::Chunked],
|
@@ -280,7 +312,9 @@ module Rack
|
|
280
312
|
|
281
313
|
# Touch the wrapped app, so that the config.ru is loaded before
|
282
314
|
# daemonization (i.e. before chdir, etc).
|
283
|
-
|
315
|
+
handle_profiling(options[:heapfile], options[:profile_mode], options[:profile_file]) do
|
316
|
+
wrapped_app
|
317
|
+
end
|
284
318
|
|
285
319
|
daemonize_app if options[:daemonize]
|
286
320
|
|
@@ -321,6 +355,44 @@ module Rack
|
|
321
355
|
app
|
322
356
|
end
|
323
357
|
|
358
|
+
def handle_profiling(heapfile, profile_mode, filename)
|
359
|
+
if heapfile
|
360
|
+
require "objspace"
|
361
|
+
ObjectSpace.trace_object_allocations_start
|
362
|
+
yield
|
363
|
+
GC.start
|
364
|
+
::File.open(heapfile, "w") { |f| ObjectSpace.dump_all(output: f) }
|
365
|
+
exit
|
366
|
+
end
|
367
|
+
|
368
|
+
if profile_mode
|
369
|
+
require "stackprof"
|
370
|
+
require "tempfile"
|
371
|
+
|
372
|
+
make_profile_name(filename) do |filename|
|
373
|
+
::File.open(filename, "w") do |f|
|
374
|
+
StackProf.run(mode: profile_mode, out: f) do
|
375
|
+
yield
|
376
|
+
end
|
377
|
+
puts "Profile written to: #{filename}"
|
378
|
+
end
|
379
|
+
end
|
380
|
+
exit
|
381
|
+
end
|
382
|
+
|
383
|
+
yield
|
384
|
+
end
|
385
|
+
|
386
|
+
def make_profile_name(filename)
|
387
|
+
if filename
|
388
|
+
yield filename
|
389
|
+
else
|
390
|
+
::Dir::Tmpname.create("profile.dump") do |tmpname, _, _|
|
391
|
+
yield tmpname
|
392
|
+
end
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
324
396
|
def build_app_from_string
|
325
397
|
Rack::Builder.new_from_string(self.options[:builder])
|
326
398
|
end
|
@@ -1,3 +1,5 @@
|
|
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
|
|
@@ -6,15 +8,62 @@ require 'time'
|
|
6
8
|
require 'rack/request'
|
7
9
|
require 'rack/response'
|
8
10
|
require 'securerandom'
|
11
|
+
require 'digest/sha2'
|
9
12
|
|
10
13
|
module Rack
|
11
14
|
|
12
15
|
module Session
|
13
16
|
|
17
|
+
class SessionId
|
18
|
+
ID_VERSION = 2
|
19
|
+
|
20
|
+
attr_reader :public_id
|
21
|
+
|
22
|
+
def initialize(public_id)
|
23
|
+
@public_id = public_id
|
24
|
+
end
|
25
|
+
|
26
|
+
def private_id
|
27
|
+
"#{ID_VERSION}::#{hash_sid(public_id)}"
|
28
|
+
end
|
29
|
+
|
30
|
+
alias :cookie_value :public_id
|
31
|
+
|
32
|
+
def empty?; false; end
|
33
|
+
def to_s; raise; end
|
34
|
+
def inspect; public_id.inspect; end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def hash_sid(sid)
|
39
|
+
Digest::SHA256.hexdigest(sid)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
14
43
|
module Abstract
|
15
44
|
# SessionHash is responsible to lazily load the session from store.
|
16
45
|
|
17
46
|
class SessionHash
|
47
|
+
using Module.new {
|
48
|
+
refine Hash do
|
49
|
+
def transform_keys(&block)
|
50
|
+
hash = {}
|
51
|
+
each do |key, value|
|
52
|
+
hash[block.call(key)] = value
|
53
|
+
end
|
54
|
+
hash
|
55
|
+
end
|
56
|
+
end
|
57
|
+
} unless {}.respond_to?(:transform_keys)
|
58
|
+
|
59
|
+
def transform_keys(&block)
|
60
|
+
hash = dup
|
61
|
+
each do |key, value|
|
62
|
+
hash[block.call(key)] = value
|
63
|
+
end
|
64
|
+
hash
|
65
|
+
end
|
66
|
+
|
18
67
|
include Enumerable
|
19
68
|
attr_writer :id
|
20
69
|
|
@@ -57,7 +106,7 @@ module Rack
|
|
57
106
|
@data[key.to_s]
|
58
107
|
end
|
59
108
|
|
60
|
-
def fetch(key, default=Unspecified, &block)
|
109
|
+
def fetch(key, default = Unspecified, &block)
|
61
110
|
load_for_read!
|
62
111
|
if default == Unspecified
|
63
112
|
@data.fetch(key.to_s, &block)
|
@@ -160,11 +209,7 @@ module Rack
|
|
160
209
|
end
|
161
210
|
|
162
211
|
def stringify_keys(other)
|
163
|
-
|
164
|
-
other.each do |key, value|
|
165
|
-
hash[key.to_s] = value
|
166
|
-
end
|
167
|
-
hash
|
212
|
+
other.transform_keys(&:to_s)
|
168
213
|
end
|
169
214
|
end
|
170
215
|
|
@@ -199,22 +244,22 @@ module Rack
|
|
199
244
|
|
200
245
|
class Persisted
|
201
246
|
DEFAULT_OPTIONS = {
|
202
|
-
:
|
203
|
-
:
|
204
|
-
:
|
205
|
-
:
|
206
|
-
:
|
207
|
-
:
|
208
|
-
:
|
209
|
-
:
|
210
|
-
:
|
211
|
-
:
|
212
|
-
:
|
247
|
+
key: RACK_SESSION,
|
248
|
+
path: '/',
|
249
|
+
domain: nil,
|
250
|
+
expire_after: nil,
|
251
|
+
secure: false,
|
252
|
+
httponly: true,
|
253
|
+
defer: false,
|
254
|
+
renew: false,
|
255
|
+
sidbits: 128,
|
256
|
+
cookie_only: true,
|
257
|
+
secure_random: ::SecureRandom
|
213
258
|
}.freeze
|
214
259
|
|
215
260
|
attr_reader :key, :default_options, :sid_secure
|
216
261
|
|
217
|
-
def initialize(app, options={})
|
262
|
+
def initialize(app, options = {})
|
218
263
|
@app = app
|
219
264
|
@default_options = self.class::DEFAULT_OPTIONS.merge(options)
|
220
265
|
@key = @default_options.delete(:key)
|
@@ -226,7 +271,7 @@ module Rack
|
|
226
271
|
context(env)
|
227
272
|
end
|
228
273
|
|
229
|
-
def context(env, app
|
274
|
+
def context(env, app = @app)
|
230
275
|
req = make_request env
|
231
276
|
prepare_session(req)
|
232
277
|
status, headers, body = app.call(req.env)
|
@@ -349,7 +394,7 @@ module Rack
|
|
349
394
|
|
350
395
|
session.send(:load!) unless loaded_session?(session)
|
351
396
|
session_id ||= session.id
|
352
|
-
session_data = session.to_hash.delete_if { |k,v| v.nil? }
|
397
|
+
session_data = session.to_hash.delete_if { |k, v| v.nil? }
|
353
398
|
|
354
399
|
if not data = write_session(req, session_id, session_data, options)
|
355
400
|
req.get_header(RACK_ERRORS).puts("Warning! #{self.class.name} failed to save session. Content dropped.")
|
@@ -357,7 +402,7 @@ module Rack
|
|
357
402
|
req.get_header(RACK_ERRORS).puts("Deferring cookie for #{session_id}") if $VERBOSE
|
358
403
|
else
|
359
404
|
cookie = Hash.new
|
360
|
-
cookie[:value] = data
|
405
|
+
cookie[:value] = cookie_value(data)
|
361
406
|
cookie[:expires] = Time.now + options[:expire_after] if options[:expire_after]
|
362
407
|
cookie[:expires] = Time.now + options[:max_age] if options[:max_age]
|
363
408
|
set_cookie(req, res, cookie.merge!(options))
|
@@ -365,6 +410,10 @@ module Rack
|
|
365
410
|
end
|
366
411
|
public :commit_session
|
367
412
|
|
413
|
+
def cookie_value(data)
|
414
|
+
data
|
415
|
+
end
|
416
|
+
|
368
417
|
# Sets the cookie back to the client with session id. We skip the cookie
|
369
418
|
# setting if the value didn't change (sid is the same) or expires was given.
|
370
419
|
|
@@ -406,6 +455,40 @@ module Rack
|
|
406
455
|
end
|
407
456
|
end
|
408
457
|
|
458
|
+
class PersistedSecure < Persisted
|
459
|
+
class SecureSessionHash < SessionHash
|
460
|
+
def [](key)
|
461
|
+
if key == "session_id"
|
462
|
+
load_for_read!
|
463
|
+
id.public_id
|
464
|
+
else
|
465
|
+
super
|
466
|
+
end
|
467
|
+
end
|
468
|
+
end
|
469
|
+
|
470
|
+
def generate_sid(*)
|
471
|
+
public_id = super
|
472
|
+
|
473
|
+
SessionId.new(public_id)
|
474
|
+
end
|
475
|
+
|
476
|
+
def extract_session_id(*)
|
477
|
+
public_id = super
|
478
|
+
public_id && SessionId.new(public_id)
|
479
|
+
end
|
480
|
+
|
481
|
+
private
|
482
|
+
|
483
|
+
def session_class
|
484
|
+
SecureSessionHash
|
485
|
+
end
|
486
|
+
|
487
|
+
def cookie_value(data)
|
488
|
+
data.cookie_value
|
489
|
+
end
|
490
|
+
end
|
491
|
+
|
409
492
|
class ID < Persisted
|
410
493
|
def self.inherited(klass)
|
411
494
|
k = klass.ancestors.find { |kl| kl.respond_to?(:superclass) && kl.superclass == ID }
|