rack 2.2.9 → 3.1.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +330 -88
  3. data/CONTRIBUTING.md +63 -55
  4. data/MIT-LICENSE +1 -1
  5. data/README.md +328 -0
  6. data/SPEC.rdoc +204 -131
  7. data/lib/rack/auth/abstract/handler.rb +3 -1
  8. data/lib/rack/auth/abstract/request.rb +3 -1
  9. data/lib/rack/auth/basic.rb +1 -4
  10. data/lib/rack/bad_request.rb +8 -0
  11. data/lib/rack/body_proxy.rb +21 -3
  12. data/lib/rack/builder.rb +102 -69
  13. data/lib/rack/cascade.rb +2 -3
  14. data/lib/rack/common_logger.rb +23 -18
  15. data/lib/rack/conditional_get.rb +18 -15
  16. data/lib/rack/constants.rb +67 -0
  17. data/lib/rack/content_length.rb +12 -16
  18. data/lib/rack/content_type.rb +8 -5
  19. data/lib/rack/deflater.rb +40 -26
  20. data/lib/rack/directory.rb +9 -3
  21. data/lib/rack/etag.rb +14 -23
  22. data/lib/rack/events.rb +4 -0
  23. data/lib/rack/files.rb +15 -17
  24. data/lib/rack/head.rb +9 -8
  25. data/lib/rack/headers.rb +238 -0
  26. data/lib/rack/lint.rb +840 -644
  27. data/lib/rack/lock.rb +2 -5
  28. data/lib/rack/logger.rb +3 -0
  29. data/lib/rack/method_override.rb +5 -1
  30. data/lib/rack/mime.rb +14 -5
  31. data/lib/rack/mock.rb +1 -271
  32. data/lib/rack/mock_request.rb +161 -0
  33. data/lib/rack/mock_response.rb +124 -0
  34. data/lib/rack/multipart/generator.rb +7 -5
  35. data/lib/rack/multipart/parser.rb +213 -95
  36. data/lib/rack/multipart/uploaded_file.rb +4 -0
  37. data/lib/rack/multipart.rb +53 -40
  38. data/lib/rack/null_logger.rb +9 -0
  39. data/lib/rack/query_parser.rb +81 -102
  40. data/lib/rack/recursive.rb +2 -0
  41. data/lib/rack/reloader.rb +0 -2
  42. data/lib/rack/request.rb +260 -123
  43. data/lib/rack/response.rb +151 -66
  44. data/lib/rack/rewindable_input.rb +24 -5
  45. data/lib/rack/runtime.rb +7 -6
  46. data/lib/rack/sendfile.rb +30 -25
  47. data/lib/rack/show_exceptions.rb +21 -4
  48. data/lib/rack/show_status.rb +17 -7
  49. data/lib/rack/static.rb +8 -8
  50. data/lib/rack/tempfile_reaper.rb +15 -4
  51. data/lib/rack/urlmap.rb +3 -1
  52. data/lib/rack/utils.rb +232 -233
  53. data/lib/rack/version.rb +1 -9
  54. data/lib/rack.rb +13 -89
  55. metadata +15 -41
  56. data/README.rdoc +0 -320
  57. data/Rakefile +0 -130
  58. data/bin/rackup +0 -5
  59. data/contrib/rack.png +0 -0
  60. data/contrib/rack.svg +0 -150
  61. data/contrib/rack_logo.svg +0 -164
  62. data/contrib/rdoc.css +0 -412
  63. data/example/lobster.ru +0 -6
  64. data/example/protectedlobster.rb +0 -16
  65. data/example/protectedlobster.ru +0 -10
  66. data/lib/rack/auth/digest/md5.rb +0 -131
  67. data/lib/rack/auth/digest/nonce.rb +0 -54
  68. data/lib/rack/auth/digest/params.rb +0 -54
  69. data/lib/rack/auth/digest/request.rb +0 -43
  70. data/lib/rack/chunked.rb +0 -117
  71. data/lib/rack/core_ext/regexp.rb +0 -14
  72. data/lib/rack/file.rb +0 -7
  73. data/lib/rack/handler/cgi.rb +0 -59
  74. data/lib/rack/handler/fastcgi.rb +0 -100
  75. data/lib/rack/handler/lsws.rb +0 -61
  76. data/lib/rack/handler/scgi.rb +0 -71
  77. data/lib/rack/handler/thin.rb +0 -36
  78. data/lib/rack/handler/webrick.rb +0 -129
  79. data/lib/rack/handler.rb +0 -104
  80. data/lib/rack/lobster.rb +0 -70
  81. data/lib/rack/server.rb +0 -466
  82. data/lib/rack/session/abstract/id.rb +0 -523
  83. data/lib/rack/session/cookie.rb +0 -204
  84. data/lib/rack/session/memcache.rb +0 -10
  85. data/lib/rack/session/pool.rb +0 -85
  86. data/rack.gemspec +0 -46
@@ -1,523 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
4
- # bugrep: Andreas Zehnder
5
-
6
- require_relative '../../../rack'
7
- require 'time'
8
- require 'securerandom'
9
- require 'digest/sha2'
10
-
11
- module Rack
12
-
13
- module Session
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
-
41
- module Abstract
42
- # SessionHash is responsible to lazily load the session from store.
43
-
44
- class SessionHash
45
- include Enumerable
46
- attr_writer :id
47
-
48
- Unspecified = Object.new
49
-
50
- def self.find(req)
51
- req.get_header RACK_SESSION
52
- end
53
-
54
- def self.set(req, session)
55
- req.set_header RACK_SESSION, session
56
- end
57
-
58
- def self.set_options(req, options)
59
- req.set_header RACK_SESSION_OPTIONS, options.dup
60
- end
61
-
62
- def initialize(store, req)
63
- @store = store
64
- @req = req
65
- @loaded = false
66
- end
67
-
68
- def id
69
- return @id if @loaded or instance_variable_defined?(:@id)
70
- @id = @store.send(:extract_session_id, @req)
71
- end
72
-
73
- def options
74
- @req.session_options
75
- end
76
-
77
- def each(&block)
78
- load_for_read!
79
- @data.each(&block)
80
- end
81
-
82
- def [](key)
83
- load_for_read!
84
- @data[key.to_s]
85
- end
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
100
-
101
- def has_key?(key)
102
- load_for_read!
103
- @data.has_key?(key.to_s)
104
- end
105
- alias :key? :has_key?
106
- alias :include? :has_key?
107
-
108
- def []=(key, value)
109
- load_for_write!
110
- @data[key.to_s] = value
111
- end
112
- alias :store :[]=
113
-
114
- def clear
115
- load_for_write!
116
- @data.clear
117
- end
118
-
119
- def destroy
120
- clear
121
- @id = @store.send(:delete_session, @req, id, options)
122
- end
123
-
124
- def to_hash
125
- load_for_read!
126
- @data.dup
127
- end
128
-
129
- def update(hash)
130
- load_for_write!
131
- @data.update(stringify_keys(hash))
132
- end
133
- alias :merge! :update
134
-
135
- def replace(hash)
136
- load_for_write!
137
- @data.replace(stringify_keys(hash))
138
- end
139
-
140
- def delete(key)
141
- load_for_write!
142
- @data.delete(key.to_s)
143
- end
144
-
145
- def inspect
146
- if loaded?
147
- @data.inspect
148
- else
149
- "#<#{self.class}:0x#{self.object_id.to_s(16)} not yet loaded>"
150
- end
151
- end
152
-
153
- def exists?
154
- return @exists if instance_variable_defined?(:@exists)
155
- @data = {}
156
- @exists = @store.send(:session_exists?, @req)
157
- end
158
-
159
- def loaded?
160
- @loaded
161
- end
162
-
163
- def empty?
164
- load_for_read!
165
- @data.empty?
166
- end
167
-
168
- def keys
169
- load_for_read!
170
- @data.keys
171
- end
172
-
173
- def values
174
- load_for_read!
175
- @data.values
176
- end
177
-
178
- private
179
-
180
- def load_for_read!
181
- load! if !loaded? && exists?
182
- end
183
-
184
- def load_for_write!
185
- load! unless loaded?
186
- end
187
-
188
- def load!
189
- @id, session = @store.send(:load_session, @req)
190
- @data = stringify_keys(session)
191
- @loaded = true
192
- end
193
-
194
- def stringify_keys(other)
195
- # Use transform_keys after dropping Ruby 2.4 support
196
- hash = {}
197
- other.to_hash.each do |key, value|
198
- hash[key.to_s] = value
199
- end
200
- hash
201
- end
202
- end
203
-
204
- # ID sets up a basic framework for implementing an id based sessioning
205
- # service. Cookies sent to the client for maintaining sessions will only
206
- # contain an id reference. Only #find_session, #write_session and
207
- # #delete_session are required to be overwritten.
208
- #
209
- # All parameters are optional.
210
- # * :key determines the name of the cookie, by default it is
211
- # 'rack.session'
212
- # * :path, :domain, :expire_after, :secure, and :httponly set the related
213
- # cookie options as by Rack::Response#set_cookie
214
- # * :skip will not a set a cookie in the response nor update the session state
215
- # * :defer will not set a cookie in the response but still update the session
216
- # state if it is used with a backend
217
- # * :renew (implementation dependent) will prompt the generation of a new
218
- # session id, and migration of data to be referenced at the new id. If
219
- # :defer is set, it will be overridden and the cookie will be set.
220
- # * :sidbits sets the number of bits in length that a generated session
221
- # id will be.
222
- #
223
- # These options can be set on a per request basis, at the location of
224
- # <tt>env['rack.session.options']</tt>. Additionally the id of the
225
- # session can be found within the options hash at the key :id. It is
226
- # highly not recommended to change its value.
227
- #
228
- # Is Rack::Utils::Context compatible.
229
- #
230
- # Not included by default; you must require 'rack/session/abstract/id'
231
- # to use.
232
-
233
- class Persisted
234
- DEFAULT_OPTIONS = {
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
247
-
248
- attr_reader :key, :default_options, :sid_secure
249
-
250
- def initialize(app, options = {})
251
- @app = app
252
- @default_options = self.class::DEFAULT_OPTIONS.merge(options)
253
- @key = @default_options.delete(:key)
254
- @cookie_only = @default_options.delete(:cookie_only)
255
- @same_site = @default_options.delete(:same_site)
256
- initialize_sid
257
- end
258
-
259
- def call(env)
260
- context(env)
261
- end
262
-
263
- def context(env, app = @app)
264
- req = make_request env
265
- prepare_session(req)
266
- status, headers, body = app.call(req.env)
267
- res = Rack::Response::Raw.new status, headers
268
- commit_session(req, res)
269
- [status, headers, body]
270
- end
271
-
272
- private
273
-
274
- def make_request(env)
275
- Rack::Request.new env
276
- end
277
-
278
- def initialize_sid
279
- @sidbits = @default_options[:sidbits]
280
- @sid_secure = @default_options[:secure_random]
281
- @sid_length = @sidbits / 4
282
- end
283
-
284
- # Generate a new session id using Ruby #rand. The size of the
285
- # session id is controlled by the :sidbits option.
286
- # Monkey patch this to use custom methods for session id generation.
287
-
288
- def generate_sid(secure = @sid_secure)
289
- if secure
290
- secure.hex(@sid_length)
291
- else
292
- "%0#{@sid_length}x" % Kernel.rand(2**@sidbits - 1)
293
- end
294
- rescue NotImplementedError
295
- generate_sid(false)
296
- end
297
-
298
- # Sets the lazy session at 'rack.session' and places options and session
299
- # metadata into 'rack.session.options'.
300
-
301
- def prepare_session(req)
302
- session_was = req.get_header RACK_SESSION
303
- session = session_class.new(self, req)
304
- req.set_header RACK_SESSION, session
305
- req.set_header RACK_SESSION_OPTIONS, @default_options.dup
306
- session.merge! session_was if session_was
307
- end
308
-
309
- # Extracts the session id from provided cookies and passes it and the
310
- # environment to #find_session.
311
-
312
- def load_session(req)
313
- sid = current_session_id(req)
314
- sid, session = find_session(req, sid)
315
- [sid, session || {}]
316
- end
317
-
318
- # Extract session id from request object.
319
-
320
- def extract_session_id(request)
321
- sid = request.cookies[@key]
322
- sid ||= request.params[@key] unless @cookie_only
323
- sid
324
- end
325
-
326
- # Returns the current session id from the SessionHash.
327
-
328
- def current_session_id(req)
329
- req.get_header(RACK_SESSION).id
330
- end
331
-
332
- # Check if the session exists or not.
333
-
334
- def session_exists?(req)
335
- value = current_session_id(req)
336
- value && !value.empty?
337
- end
338
-
339
- # Session should be committed if it was loaded, any of specific options like :renew, :drop
340
- # or :expire_after was given and the security permissions match. Skips if skip is given.
341
-
342
- def commit_session?(req, session, options)
343
- if options[:skip]
344
- false
345
- else
346
- has_session = loaded_session?(session) || forced_session_update?(session, options)
347
- has_session && security_matches?(req, options)
348
- end
349
- end
350
-
351
- def loaded_session?(session)
352
- !session.is_a?(session_class) || session.loaded?
353
- end
354
-
355
- def forced_session_update?(session, options)
356
- force_options?(options) && session && !session.empty?
357
- end
358
-
359
- def force_options?(options)
360
- options.values_at(:max_age, :renew, :drop, :defer, :expire_after).any?
361
- end
362
-
363
- def security_matches?(request, options)
364
- return true unless options[:secure]
365
- request.ssl?
366
- end
367
-
368
- # Acquires the session from the environment and the session id from
369
- # the session options and passes them to #write_session. If successful
370
- # and the :defer option is not true, a cookie will be added to the
371
- # response with the session's id.
372
-
373
- def commit_session(req, res)
374
- session = req.get_header RACK_SESSION
375
- options = session.options
376
-
377
- if options[:drop] || options[:renew]
378
- session_id = delete_session(req, session.id || generate_sid, options)
379
- return unless session_id
380
- end
381
-
382
- return unless commit_session?(req, session, options)
383
-
384
- session.send(:load!) unless loaded_session?(session)
385
- session_id ||= session.id
386
- session_data = session.to_hash.delete_if { |k, v| v.nil? }
387
-
388
- if not data = write_session(req, session_id, session_data, options)
389
- req.get_header(RACK_ERRORS).puts("Warning! #{self.class.name} failed to save session. Content dropped.")
390
- elsif options[:defer] and not options[:renew]
391
- req.get_header(RACK_ERRORS).puts("Deferring cookie for #{session_id}") if $VERBOSE
392
- else
393
- cookie = Hash.new
394
- cookie[:value] = cookie_value(data)
395
- cookie[:expires] = Time.now + options[:expire_after] if options[:expire_after]
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
403
- set_cookie(req, res, cookie.merge!(options))
404
- end
405
- end
406
- public :commit_session
407
-
408
- def cookie_value(data)
409
- data
410
- end
411
-
412
- # Sets the cookie back to the client with session id. We skip the cookie
413
- # setting if the value didn't change (sid is the same) or expires was given.
414
-
415
- def set_cookie(request, res, cookie)
416
- if request.cookies[@key] != cookie[:value] || cookie[:expires]
417
- res.set_cookie_header =
418
- Utils.add_cookie_to_header(res.set_cookie_header, @key, cookie)
419
- end
420
- end
421
-
422
- # Allow subclasses to prepare_session for different Session classes
423
-
424
- def session_class
425
- SessionHash
426
- end
427
-
428
- # All thread safety and session retrieval procedures should occur here.
429
- # Should return [session_id, session].
430
- # If nil is provided as the session id, generation of a new valid id
431
- # should occur within.
432
-
433
- def find_session(env, sid)
434
- raise '#find_session not implemented.'
435
- end
436
-
437
- # All thread safety and session storage procedures should occur here.
438
- # Must return the session id if the session was saved successfully, or
439
- # false if the session could not be saved.
440
-
441
- def write_session(req, sid, session, options)
442
- raise '#write_session not implemented.'
443
- end
444
-
445
- # All thread safety and session destroy procedures should occur here.
446
- # Should return a new session id or nil if options[:drop]
447
-
448
- def delete_session(req, sid, options)
449
- raise '#delete_session not implemented'
450
- end
451
- end
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
-
487
- class ID < Persisted
488
- def self.inherited(klass)
489
- k = klass.ancestors.find { |kl| kl.respond_to?(:superclass) && kl.superclass == ID }
490
- unless k.instance_variable_defined?(:"@_rack_warned")
491
- warn "#{klass} is inheriting from #{ID}. Inheriting from #{ID} is deprecated, please inherit from #{Persisted} instead" if $VERBOSE
492
- k.instance_variable_set(:"@_rack_warned", true)
493
- end
494
- super
495
- end
496
-
497
- # All thread safety and session retrieval procedures should occur here.
498
- # Should return [session_id, session].
499
- # If nil is provided as the session id, generation of a new valid id
500
- # should occur within.
501
-
502
- def find_session(req, sid)
503
- get_session req.env, sid
504
- end
505
-
506
- # All thread safety and session storage procedures should occur here.
507
- # Must return the session id if the session was saved successfully, or
508
- # false if the session could not be saved.
509
-
510
- def write_session(req, sid, session, options)
511
- set_session req.env, sid, session, options
512
- end
513
-
514
- # All thread safety and session destroy procedures should occur here.
515
- # Should return a new session id or nil if options[:drop]
516
-
517
- def delete_session(req, sid, options)
518
- destroy_session req.env, sid, options
519
- end
520
- end
521
- end
522
- end
523
- end