rack 2.0.1 → 2.2.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG.md +795 -0
- data/CONTRIBUTING.md +136 -0
- data/{COPYING → MIT-LICENSE} +4 -2
- data/README.rdoc +188 -145
- data/Rakefile +37 -23
- data/{SPEC → SPEC.rdoc} +46 -17
- 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 +6 -4
- data/lib/rack/auth/digest/md5.rb +13 -11
- data/lib/rack/auth/digest/nonce.rb +5 -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 +37 -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 +60 -70
- data/lib/rack/directory.rb +84 -64
- data/lib/rack/etag.rb +8 -5
- 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 +19 -10
- data/lib/rack/handler.rb +7 -2
- data/lib/rack/head.rb +1 -1
- data/lib/rack/lint.rb +221 -186
- data/lib/rack/lobster.rb +10 -10
- data/lib/rack/lock.rb +14 -4
- data/lib/rack/logger.rb +2 -0
- data/lib/rack/media_type.rb +23 -8
- data/lib/rack/method_override.rb +13 -4
- data/lib/rack/mime.rb +9 -1
- data/lib/rack/mock.rb +135 -29
- data/lib/rack/multipart/generator.rb +17 -13
- data/lib/rack/multipart/parser.rb +85 -68
- data/lib/rack/multipart/uploaded_file.rb +15 -7
- data/lib/rack/multipart.rb +6 -5
- data/lib/rack/null_logger.rb +2 -0
- data/lib/rack/query_parser.rb +108 -36
- data/lib/rack/recursive.rb +7 -5
- data/lib/rack/reloader.rb +8 -4
- data/lib/rack/request.rb +232 -60
- 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 +14 -10
- data/lib/rack/server.rb +97 -25
- data/lib/rack/session/abstract/id.rb +113 -25
- data/lib/rack/session/cookie.rb +22 -14
- data/lib/rack/session/memcache.rb +4 -87
- data/lib/rack/session/pool.rb +24 -10
- data/lib/rack/show_exceptions.rb +22 -18
- data/lib/rack/show_status.rb +9 -9
- data/lib/rack/static.rb +25 -12
- data/lib/rack/tempfile_reaper.rb +1 -1
- data/lib/rack/urlmap.rb +13 -7
- data/lib/rack/utils.rb +135 -123
- data/lib/rack/version.rb +29 -0
- data/lib/rack.rb +67 -73
- data/rack.gemspec +40 -29
- metadata +25 -184
- data/HISTORY.md +0 -505
- 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_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 -365
- 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 -251
- 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 -194
- data/test/spec_logger.rb +0 -24
- data/test/spec_media_type.rb +0 -42
- data/test/spec_method_override.rb +0 -83
- data/test/spec_mime.rb +0 -51
- data/test/spec_mock.rb +0 -342
- data/test/spec_multipart.rb +0 -716
- 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 -28
- 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 -208
- 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,16 +1,43 @@
|
|
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
|
+
require 'digest/sha2'
|
9
10
|
|
10
11
|
module Rack
|
11
12
|
|
12
13
|
module Session
|
13
14
|
|
15
|
+
class SessionId
|
16
|
+
ID_VERSION = 2
|
17
|
+
|
18
|
+
attr_reader :public_id
|
19
|
+
|
20
|
+
def initialize(public_id)
|
21
|
+
@public_id = public_id
|
22
|
+
end
|
23
|
+
|
24
|
+
def private_id
|
25
|
+
"#{ID_VERSION}::#{hash_sid(public_id)}"
|
26
|
+
end
|
27
|
+
|
28
|
+
alias :cookie_value :public_id
|
29
|
+
alias :to_s :public_id
|
30
|
+
|
31
|
+
def empty?; false; end
|
32
|
+
def inspect; public_id.inspect; end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def hash_sid(sid)
|
37
|
+
Digest::SHA256.hexdigest(sid)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
14
41
|
module Abstract
|
15
42
|
# SessionHash is responsible to lazily load the session from store.
|
16
43
|
|
@@ -18,6 +45,8 @@ module Rack
|
|
18
45
|
include Enumerable
|
19
46
|
attr_writer :id
|
20
47
|
|
48
|
+
Unspecified = Object.new
|
49
|
+
|
21
50
|
def self.find(req)
|
22
51
|
req.get_header RACK_SESSION
|
23
52
|
end
|
@@ -54,7 +83,20 @@ module Rack
|
|
54
83
|
load_for_read!
|
55
84
|
@data[key.to_s]
|
56
85
|
end
|
57
|
-
|
86
|
+
|
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)
|
93
|
+
load_for_read!
|
94
|
+
if default == Unspecified
|
95
|
+
@data.fetch(key.to_s, &block)
|
96
|
+
else
|
97
|
+
@data.fetch(key.to_s, default, &block)
|
98
|
+
end
|
99
|
+
end
|
58
100
|
|
59
101
|
def has_key?(key)
|
60
102
|
load_for_read!
|
@@ -150,8 +192,9 @@ module Rack
|
|
150
192
|
end
|
151
193
|
|
152
194
|
def stringify_keys(other)
|
195
|
+
# Use transform_keys after dropping Ruby 2.4 support
|
153
196
|
hash = {}
|
154
|
-
other.each do |key, value|
|
197
|
+
other.to_hash.each do |key, value|
|
155
198
|
hash[key.to_s] = value
|
156
199
|
end
|
157
200
|
hash
|
@@ -160,14 +203,14 @@ module Rack
|
|
160
203
|
|
161
204
|
# ID sets up a basic framework for implementing an id based sessioning
|
162
205
|
# service. Cookies sent to the client for maintaining sessions will only
|
163
|
-
# contain an id reference. Only #find_session
|
164
|
-
# required to be overwritten.
|
206
|
+
# contain an id reference. Only #find_session, #write_session and
|
207
|
+
# #delete_session are required to be overwritten.
|
165
208
|
#
|
166
209
|
# All parameters are optional.
|
167
210
|
# * :key determines the name of the cookie, by default it is
|
168
211
|
# 'rack.session'
|
169
212
|
# * :path, :domain, :expire_after, :secure, and :httponly set the related
|
170
|
-
# cookie options as by Rack::Response#
|
213
|
+
# cookie options as by Rack::Response#set_cookie
|
171
214
|
# * :skip will not a set a cookie in the response nor update the session state
|
172
215
|
# * :defer will not set a cookie in the response but still update the session
|
173
216
|
# state if it is used with a backend
|
@@ -189,26 +232,27 @@ module Rack
|
|
189
232
|
|
190
233
|
class Persisted
|
191
234
|
DEFAULT_OPTIONS = {
|
192
|
-
:
|
193
|
-
:
|
194
|
-
:
|
195
|
-
:
|
196
|
-
:
|
197
|
-
:
|
198
|
-
:
|
199
|
-
:
|
200
|
-
:
|
201
|
-
:
|
202
|
-
:
|
203
|
-
}
|
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
|
246
|
+
}.freeze
|
204
247
|
|
205
248
|
attr_reader :key, :default_options, :sid_secure
|
206
249
|
|
207
|
-
def initialize(app, options={})
|
250
|
+
def initialize(app, options = {})
|
208
251
|
@app = app
|
209
252
|
@default_options = self.class::DEFAULT_OPTIONS.merge(options)
|
210
253
|
@key = @default_options.delete(:key)
|
211
254
|
@cookie_only = @default_options.delete(:cookie_only)
|
255
|
+
@same_site = @default_options.delete(:same_site)
|
212
256
|
initialize_sid
|
213
257
|
end
|
214
258
|
|
@@ -216,7 +260,7 @@ module Rack
|
|
216
260
|
context(env)
|
217
261
|
end
|
218
262
|
|
219
|
-
def context(env, app
|
263
|
+
def context(env, app = @app)
|
220
264
|
req = make_request env
|
221
265
|
prepare_session(req)
|
222
266
|
status, headers, body = app.call(req.env)
|
@@ -339,7 +383,7 @@ module Rack
|
|
339
383
|
|
340
384
|
session.send(:load!) unless loaded_session?(session)
|
341
385
|
session_id ||= session.id
|
342
|
-
session_data = session.to_hash.delete_if { |k,v| v.nil? }
|
386
|
+
session_data = session.to_hash.delete_if { |k, v| v.nil? }
|
343
387
|
|
344
388
|
if not data = write_session(req, session_id, session_data, options)
|
345
389
|
req.get_header(RACK_ERRORS).puts("Warning! #{self.class.name} failed to save session. Content dropped.")
|
@@ -347,14 +391,24 @@ module Rack
|
|
347
391
|
req.get_header(RACK_ERRORS).puts("Deferring cookie for #{session_id}") if $VERBOSE
|
348
392
|
else
|
349
393
|
cookie = Hash.new
|
350
|
-
cookie[:value] = data
|
394
|
+
cookie[:value] = cookie_value(data)
|
351
395
|
cookie[:expires] = Time.now + options[:expire_after] if options[:expire_after]
|
352
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
|
353
403
|
set_cookie(req, res, cookie.merge!(options))
|
354
404
|
end
|
355
405
|
end
|
356
406
|
public :commit_session
|
357
407
|
|
408
|
+
def cookie_value(data)
|
409
|
+
data
|
410
|
+
end
|
411
|
+
|
358
412
|
# Sets the cookie back to the client with session id. We skip the cookie
|
359
413
|
# setting if the value didn't change (sid is the same) or expires was given.
|
360
414
|
|
@@ -396,9 +450,43 @@ module Rack
|
|
396
450
|
end
|
397
451
|
end
|
398
452
|
|
453
|
+
class PersistedSecure < Persisted
|
454
|
+
class SecureSessionHash < SessionHash
|
455
|
+
def [](key)
|
456
|
+
if key == "session_id"
|
457
|
+
load_for_read!
|
458
|
+
id.public_id if id
|
459
|
+
else
|
460
|
+
super
|
461
|
+
end
|
462
|
+
end
|
463
|
+
end
|
464
|
+
|
465
|
+
def generate_sid(*)
|
466
|
+
public_id = super
|
467
|
+
|
468
|
+
SessionId.new(public_id)
|
469
|
+
end
|
470
|
+
|
471
|
+
def extract_session_id(*)
|
472
|
+
public_id = super
|
473
|
+
public_id && SessionId.new(public_id)
|
474
|
+
end
|
475
|
+
|
476
|
+
private
|
477
|
+
|
478
|
+
def session_class
|
479
|
+
SecureSessionHash
|
480
|
+
end
|
481
|
+
|
482
|
+
def cookie_value(data)
|
483
|
+
data.cookie_value
|
484
|
+
end
|
485
|
+
end
|
486
|
+
|
399
487
|
class ID < Persisted
|
400
488
|
def self.inherited(klass)
|
401
|
-
k = klass.ancestors.find { |kl| kl.superclass == ID }
|
489
|
+
k = klass.ancestors.find { |kl| kl.respond_to?(:superclass) && kl.superclass == ID }
|
402
490
|
unless k.instance_variable_defined?(:"@_rack_warned")
|
403
491
|
warn "#{klass} is inheriting from #{ID}. Inheriting from #{ID} is deprecated, please inherit from #{Persisted} instead" if $VERBOSE
|
404
492
|
k.instance_variable_set(:"@_rack_warned", true)
|
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 'delegate'
|
7
8
|
|
8
9
|
module Rack
|
9
10
|
|
@@ -45,15 +46,15 @@ module Rack
|
|
45
46
|
# })
|
46
47
|
#
|
47
48
|
|
48
|
-
class Cookie < Abstract::
|
49
|
+
class Cookie < Abstract::PersistedSecure
|
49
50
|
# Encode session cookies as Base64
|
50
51
|
class Base64
|
51
52
|
def encode(str)
|
52
|
-
[str].pack(
|
53
|
+
[str].pack("m0")
|
53
54
|
end
|
54
55
|
|
55
56
|
def decode(str)
|
56
|
-
str.unpack(
|
57
|
+
str.unpack("m").first
|
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,8 @@ module Rack
|
|
116
117
|
|
117
118
|
Called from: #{caller[0]}.
|
118
119
|
MSG
|
119
|
-
@coder
|
120
|
-
super(app, options.merge!(:
|
120
|
+
@coder = options[:coder] ||= Base64::Marshal.new
|
121
|
+
super(app, options.merge!(cookie_only: true))
|
121
122
|
end
|
122
123
|
|
123
124
|
private
|
@@ -137,9 +138,7 @@ module Rack
|
|
137
138
|
session_data = request.cookies[@key]
|
138
139
|
|
139
140
|
if @secrets.size > 0 && session_data
|
140
|
-
|
141
|
-
digest.reverse! if digest
|
142
|
-
session_data.reverse! if session_data
|
141
|
+
session_data, _, digest = session_data.rpartition('--')
|
143
142
|
session_data = nil unless digest_match?(session_data, digest)
|
144
143
|
end
|
145
144
|
|
@@ -147,12 +146,21 @@ module Rack
|
|
147
146
|
end
|
148
147
|
end
|
149
148
|
|
150
|
-
def persistent_session_id!(data, sid=nil)
|
149
|
+
def persistent_session_id!(data, sid = nil)
|
151
150
|
data ||= {}
|
152
151
|
data["session_id"] ||= sid || generate_sid
|
153
152
|
data
|
154
153
|
end
|
155
154
|
|
155
|
+
class SessionId < DelegateClass(Session::SessionId)
|
156
|
+
attr_reader :cookie_value
|
157
|
+
|
158
|
+
def initialize(session_id, cookie_value)
|
159
|
+
super(session_id)
|
160
|
+
@cookie_value = cookie_value
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
156
164
|
def write_session(req, session_id, session, options)
|
157
165
|
session = session.merge("session_id" => session_id)
|
158
166
|
session_data = coder.encode(session)
|
@@ -165,7 +173,7 @@ module Rack
|
|
165
173
|
req.get_header(RACK_ERRORS).puts("Warning! Rack::Session::Cookie data size exceeds 4K.")
|
166
174
|
nil
|
167
175
|
else
|
168
|
-
session_data
|
176
|
+
SessionId.new(session_id, session_data)
|
169
177
|
end
|
170
178
|
end
|
171
179
|
|
@@ -1,93 +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::ID
|
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, true)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def get_session(env, sid)
|
50
|
-
with_lock(env) do
|
51
|
-
unless sid and session = @pool.get(sid)
|
52
|
-
sid, session = generate_sid, {}
|
53
|
-
unless /^STORED/ =~ @pool.add(sid, session)
|
54
|
-
raise "Session collision on '#{sid.inspect}'"
|
55
|
-
end
|
56
|
-
end
|
57
|
-
[sid, session]
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def set_session(env, session_id, new_session, options)
|
62
|
-
expiry = options[:expire_after]
|
63
|
-
expiry = expiry.nil? ? 0 : expiry + 1
|
64
|
-
|
65
|
-
with_lock(env) do
|
66
|
-
@pool.set session_id, new_session, expiry
|
67
|
-
session_id
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
def destroy_session(env, session_id, options)
|
72
|
-
with_lock(env) do
|
73
|
-
@pool.delete(session_id)
|
74
|
-
generate_sid unless options[:drop]
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def with_lock(env)
|
79
|
-
@mutex.lock if env[RACK_MULTITHREAD]
|
80
|
-
yield
|
81
|
-
rescue MemCache::MemCacheError, Errno::ECONNREFUSED
|
82
|
-
if $VERBOSE
|
83
|
-
warn "#{self} is unable to find memcached server."
|
84
|
-
warn $!.inspect
|
85
|
-
end
|
86
|
-
raise
|
87
|
-
ensure
|
88
|
-
@mutex.unlock if @mutex.locked?
|
89
|
-
end
|
90
|
-
|
91
|
-
end
|
7
|
+
warn "Rack::Session::Memcache is deprecated, please use Rack::Session::Dalli from 'dalli' gem instead."
|
8
|
+
Memcache = Dalli
|
92
9
|
end
|
93
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
|
@@ -24,11 +26,11 @@ module Rack
|
|
24
26
|
# )
|
25
27
|
# Rack::Handler::WEBrick.run sessioned
|
26
28
|
|
27
|
-
class Pool < Abstract::
|
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
|
@@ -37,15 +39,15 @@ module Rack
|
|
37
39
|
def generate_sid
|
38
40
|
loop do
|
39
41
|
sid = super
|
40
|
-
break sid unless @pool.key? sid
|
42
|
+
break sid unless @pool.key? sid.private_id
|
41
43
|
end
|
42
44
|
end
|
43
45
|
|
44
46
|
def find_session(req, sid)
|
45
47
|
with_lock(req) do
|
46
|
-
unless sid and session =
|
48
|
+
unless sid and session = get_session_with_fallback(sid)
|
47
49
|
sid, session = generate_sid, {}
|
48
|
-
@pool.store sid, session
|
50
|
+
@pool.store sid.private_id, session
|
49
51
|
end
|
50
52
|
[sid, session]
|
51
53
|
end
|
@@ -53,15 +55,21 @@ module Rack
|
|
53
55
|
|
54
56
|
def write_session(req, session_id, new_session, options)
|
55
57
|
with_lock(req) do
|
56
|
-
|
58
|
+
return false unless get_session_with_fallback(session_id)
|
59
|
+
@pool.store session_id.private_id, new_session
|
57
60
|
session_id
|
58
61
|
end
|
59
62
|
end
|
60
63
|
|
61
64
|
def delete_session(req, session_id, options)
|
62
65
|
with_lock(req) do
|
63
|
-
@pool.delete(session_id)
|
64
|
-
|
66
|
+
@pool.delete(session_id.public_id)
|
67
|
+
@pool.delete(session_id.private_id)
|
68
|
+
unless options[:drop]
|
69
|
+
sid = generate_sid
|
70
|
+
@pool.store(sid.private_id, {})
|
71
|
+
sid
|
72
|
+
end
|
65
73
|
end
|
66
74
|
end
|
67
75
|
|
@@ -71,6 +79,12 @@ module Rack
|
|
71
79
|
ensure
|
72
80
|
@mutex.unlock if @mutex.locked?
|
73
81
|
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def get_session_with_fallback(sid)
|
86
|
+
@pool[sid.private_id] || @pool[sid.public_id]
|
87
|
+
end
|
74
88
|
end
|
75
89
|
end
|
76
90
|
end
|
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
|
@@ -46,7 +46,7 @@ module Rack
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def prefers_plaintext?(env)
|
49
|
-
!accepts_html(env)
|
49
|
+
!accepts_html?(env)
|
50
50
|
end
|
51
51
|
|
52
52
|
def accepts_html?(env)
|
@@ -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'
|