rack 1.4.7 → 2.1.4

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.

Files changed (183) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +77 -0
  3. data/{COPYING → MIT-LICENSE} +4 -2
  4. data/README.rdoc +122 -456
  5. data/Rakefile +32 -31
  6. data/SPEC +119 -29
  7. data/bin/rackup +1 -0
  8. data/contrib/rack_logo.svg +164 -111
  9. data/example/lobster.ru +2 -0
  10. data/example/protectedlobster.rb +4 -2
  11. data/example/protectedlobster.ru +3 -1
  12. data/lib/rack/auth/abstract/handler.rb +7 -5
  13. data/lib/rack/auth/abstract/request.rb +8 -6
  14. data/lib/rack/auth/basic.rb +5 -2
  15. data/lib/rack/auth/digest/md5.rb +10 -8
  16. data/lib/rack/auth/digest/nonce.rb +6 -3
  17. data/lib/rack/auth/digest/params.rb +5 -4
  18. data/lib/rack/auth/digest/request.rb +4 -2
  19. data/lib/rack/body_proxy.rb +11 -9
  20. data/lib/rack/builder.rb +63 -20
  21. data/lib/rack/cascade.rb +10 -9
  22. data/lib/rack/chunked.rb +45 -11
  23. data/lib/rack/{commonlogger.rb → common_logger.rb} +24 -15
  24. data/lib/rack/{conditionalget.rb → conditional_get.rb} +20 -6
  25. data/lib/rack/config.rb +7 -0
  26. data/lib/rack/content_length.rb +12 -6
  27. data/lib/rack/content_type.rb +4 -2
  28. data/lib/rack/core_ext/regexp.rb +14 -0
  29. data/lib/rack/deflater.rb +73 -42
  30. data/lib/rack/directory.rb +77 -56
  31. data/lib/rack/etag.rb +25 -13
  32. data/lib/rack/events.rb +156 -0
  33. data/lib/rack/file.rb +4 -143
  34. data/lib/rack/files.rb +178 -0
  35. data/lib/rack/handler/cgi.rb +18 -17
  36. data/lib/rack/handler/fastcgi.rb +21 -17
  37. data/lib/rack/handler/lsws.rb +14 -12
  38. data/lib/rack/handler/scgi.rb +27 -21
  39. data/lib/rack/handler/thin.rb +19 -5
  40. data/lib/rack/handler/webrick.rb +66 -24
  41. data/lib/rack/handler.rb +29 -19
  42. data/lib/rack/head.rb +21 -14
  43. data/lib/rack/lint.rb +259 -65
  44. data/lib/rack/lobster.rb +17 -10
  45. data/lib/rack/lock.rb +19 -10
  46. data/lib/rack/logger.rb +4 -2
  47. data/lib/rack/media_type.rb +43 -0
  48. data/lib/rack/method_override.rb +52 -0
  49. data/lib/rack/mime.rb +43 -6
  50. data/lib/rack/mock.rb +109 -44
  51. data/lib/rack/multipart/generator.rb +11 -12
  52. data/lib/rack/multipart/parser.rb +302 -115
  53. data/lib/rack/multipart/uploaded_file.rb +4 -3
  54. data/lib/rack/multipart.rb +40 -9
  55. data/lib/rack/null_logger.rb +39 -0
  56. data/lib/rack/query_parser.rb +218 -0
  57. data/lib/rack/recursive.rb +14 -11
  58. data/lib/rack/reloader.rb +12 -5
  59. data/lib/rack/request.rb +484 -270
  60. data/lib/rack/response.rb +196 -77
  61. data/lib/rack/rewindable_input.rb +5 -14
  62. data/lib/rack/runtime.rb +13 -6
  63. data/lib/rack/sendfile.rb +44 -20
  64. data/lib/rack/server.rb +175 -61
  65. data/lib/rack/session/abstract/id.rb +276 -133
  66. data/lib/rack/session/cookie.rb +75 -40
  67. data/lib/rack/session/memcache.rb +4 -87
  68. data/lib/rack/session/pool.rb +24 -18
  69. data/lib/rack/show_exceptions.rb +392 -0
  70. data/lib/rack/{showstatus.rb → show_status.rb} +11 -9
  71. data/lib/rack/static.rb +65 -38
  72. data/lib/rack/tempfile_reaper.rb +24 -0
  73. data/lib/rack/urlmap.rb +40 -15
  74. data/lib/rack/utils.rb +316 -285
  75. data/lib/rack.rb +78 -23
  76. data/rack.gemspec +26 -19
  77. metadata +44 -209
  78. data/KNOWN-ISSUES +0 -30
  79. data/lib/rack/backports/uri/common_18.rb +0 -56
  80. data/lib/rack/backports/uri/common_192.rb +0 -52
  81. data/lib/rack/backports/uri/common_193.rb +0 -29
  82. data/lib/rack/handler/evented_mongrel.rb +0 -8
  83. data/lib/rack/handler/mongrel.rb +0 -100
  84. data/lib/rack/handler/swiftiplied_mongrel.rb +0 -8
  85. data/lib/rack/methodoverride.rb +0 -33
  86. data/lib/rack/nulllogger.rb +0 -18
  87. data/lib/rack/showexceptions.rb +0 -378
  88. data/test/builder/anything.rb +0 -5
  89. data/test/builder/comment.ru +0 -4
  90. data/test/builder/end.ru +0 -5
  91. data/test/builder/line.ru +0 -1
  92. data/test/builder/options.ru +0 -2
  93. data/test/cgi/assets/folder/test.js +0 -1
  94. data/test/cgi/assets/fonts/font.eot +0 -1
  95. data/test/cgi/assets/images/image.png +0 -1
  96. data/test/cgi/assets/index.html +0 -1
  97. data/test/cgi/assets/javascripts/app.js +0 -1
  98. data/test/cgi/assets/stylesheets/app.css +0 -1
  99. data/test/cgi/lighttpd.conf +0 -26
  100. data/test/cgi/lighttpd.errors +0 -1
  101. data/test/cgi/rackup_stub.rb +0 -6
  102. data/test/cgi/sample_rackup.ru +0 -5
  103. data/test/cgi/test +0 -9
  104. data/test/cgi/test+directory/test+file +0 -1
  105. data/test/cgi/test.fcgi +0 -8
  106. data/test/cgi/test.ru +0 -5
  107. data/test/gemloader.rb +0 -10
  108. data/test/multipart/bad_robots +0 -259
  109. data/test/multipart/binary +0 -0
  110. data/test/multipart/content_type_and_no_filename +0 -6
  111. data/test/multipart/empty +0 -10
  112. data/test/multipart/fail_16384_nofile +0 -814
  113. data/test/multipart/file1.txt +0 -1
  114. data/test/multipart/filename_and_modification_param +0 -7
  115. data/test/multipart/filename_with_escaped_quotes +0 -6
  116. data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
  117. data/test/multipart/filename_with_percent_escaped_quotes +0 -6
  118. data/test/multipart/filename_with_unescaped_percentages +0 -6
  119. data/test/multipart/filename_with_unescaped_percentages2 +0 -6
  120. data/test/multipart/filename_with_unescaped_percentages3 +0 -6
  121. data/test/multipart/filename_with_unescaped_quotes +0 -6
  122. data/test/multipart/ie +0 -6
  123. data/test/multipart/mixed_files +0 -21
  124. data/test/multipart/nested +0 -10
  125. data/test/multipart/none +0 -9
  126. data/test/multipart/semicolon +0 -6
  127. data/test/multipart/text +0 -15
  128. data/test/multipart/three_files_three_fields +0 -31
  129. data/test/multipart/webkit +0 -32
  130. data/test/rackup/config.ru +0 -31
  131. data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
  132. data/test/spec_auth.rb +0 -57
  133. data/test/spec_auth_basic.rb +0 -81
  134. data/test/spec_auth_digest.rb +0 -259
  135. data/test/spec_body_proxy.rb +0 -69
  136. data/test/spec_builder.rb +0 -207
  137. data/test/spec_cascade.rb +0 -61
  138. data/test/spec_cgi.rb +0 -102
  139. data/test/spec_chunked.rb +0 -87
  140. data/test/spec_commonlogger.rb +0 -57
  141. data/test/spec_conditionalget.rb +0 -102
  142. data/test/spec_config.rb +0 -22
  143. data/test/spec_content_length.rb +0 -86
  144. data/test/spec_content_type.rb +0 -45
  145. data/test/spec_deflater.rb +0 -187
  146. data/test/spec_directory.rb +0 -88
  147. data/test/spec_etag.rb +0 -98
  148. data/test/spec_fastcgi.rb +0 -107
  149. data/test/spec_file.rb +0 -200
  150. data/test/spec_handler.rb +0 -59
  151. data/test/spec_head.rb +0 -48
  152. data/test/spec_lint.rb +0 -515
  153. data/test/spec_lobster.rb +0 -58
  154. data/test/spec_lock.rb +0 -167
  155. data/test/spec_logger.rb +0 -23
  156. data/test/spec_methodoverride.rb +0 -72
  157. data/test/spec_mock.rb +0 -269
  158. data/test/spec_mongrel.rb +0 -182
  159. data/test/spec_multipart.rb +0 -479
  160. data/test/spec_nulllogger.rb +0 -23
  161. data/test/spec_recursive.rb +0 -72
  162. data/test/spec_request.rb +0 -955
  163. data/test/spec_response.rb +0 -313
  164. data/test/spec_rewindable_input.rb +0 -118
  165. data/test/spec_runtime.rb +0 -49
  166. data/test/spec_sendfile.rb +0 -90
  167. data/test/spec_server.rb +0 -121
  168. data/test/spec_session_abstract_id.rb +0 -43
  169. data/test/spec_session_cookie.rb +0 -361
  170. data/test/spec_session_memcache.rb +0 -321
  171. data/test/spec_session_pool.rb +0 -209
  172. data/test/spec_showexceptions.rb +0 -92
  173. data/test/spec_showstatus.rb +0 -84
  174. data/test/spec_static.rb +0 -145
  175. data/test/spec_thin.rb +0 -86
  176. data/test/spec_urlmap.rb +0 -213
  177. data/test/spec_utils.rb +0 -554
  178. data/test/spec_webrick.rb +0 -143
  179. data/test/static/another/index.html +0 -1
  180. data/test/static/index.html +0 -1
  181. data/test/testrequest.rb +0 -78
  182. data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
  183. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
@@ -1,7 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'openssl'
4
+ require 'zlib'
2
5
  require 'rack/request'
3
6
  require 'rack/response'
4
7
  require 'rack/session/abstract/id'
8
+ require 'json'
9
+ require 'base64'
5
10
 
6
11
  module Rack
7
12
 
@@ -43,15 +48,15 @@ module Rack
43
48
  # })
44
49
  #
45
50
 
46
- class Cookie < Abstract::ID
51
+ class Cookie < Abstract::PersistedSecure
47
52
  # Encode session cookies as Base64
48
53
  class Base64
49
54
  def encode(str)
50
- [str].pack('m')
55
+ ::Base64.strict_encode64(str)
51
56
  end
52
57
 
53
58
  def decode(str)
54
- str.unpack('m').first
59
+ ::Base64.decode64(str)
55
60
  end
56
61
 
57
62
  # Encode session cookies as Marshaled Base64 data
@@ -61,9 +66,36 @@ module Rack
61
66
  end
62
67
 
63
68
  def decode(str)
69
+ return unless str
64
70
  ::Marshal.load(super(str)) rescue nil
65
71
  end
66
72
  end
73
+
74
+ # N.B. Unlike other encoding methods, the contained objects must be a
75
+ # valid JSON composite type, either a Hash or an Array.
76
+ class JSON < Base64
77
+ def encode(obj)
78
+ super(::JSON.dump(obj))
79
+ end
80
+
81
+ def decode(str)
82
+ return unless str
83
+ ::JSON.parse(super(str)) rescue nil
84
+ end
85
+ end
86
+
87
+ class ZipJSON < Base64
88
+ def encode(obj)
89
+ super(Zlib::Deflate.deflate(::JSON.dump(obj)))
90
+ end
91
+
92
+ def decode(str)
93
+ return unless str
94
+ ::JSON.parse(Zlib::Inflate.inflate(super(str)))
95
+ rescue
96
+ nil
97
+ end
98
+ end
67
99
  end
68
100
 
69
101
  # Use no encoding for session cookies
@@ -72,17 +104,13 @@ module Rack
72
104
  def decode(str); str; end
73
105
  end
74
106
 
75
- # Reverse string encoding. (trollface)
76
- class Reverse
77
- def encode(str); str.reverse; end
78
- def decode(str); str.reverse; end
79
- end
80
-
81
107
  attr_reader :coder
82
108
 
83
- def initialize(app, options={})
109
+ def initialize(app, options = {})
84
110
  @secrets = options.values_at(:secret, :old_secret).compact
85
- warn <<-MSG unless @secrets.size >= 1
111
+ @hmac = options.fetch(:hmac, OpenSSL::Digest::SHA1)
112
+
113
+ warn <<-MSG unless secure?(options)
86
114
  SECURITY WARNING: No secret option provided to Rack::Session::Cookie.
87
115
  This poses a security threat. It is strongly recommended that you
88
116
  provide a secret to prevent exploits that may be possible from crafted
@@ -91,78 +119,85 @@ module Rack
91
119
 
92
120
  Called from: #{caller[0]}.
93
121
  MSG
94
- @coder = options[:coder] ||= Base64::Marshal.new
95
- super(app, options.merge!(:cookie_only => true))
122
+ @coder = options[:coder] ||= Base64::Marshal.new
123
+ super(app, options.merge!(cookie_only: true))
96
124
  end
97
125
 
98
126
  private
99
127
 
100
- def load_session(env)
101
- data = unpacked_cookie_data(env)
128
+ def find_session(req, sid)
129
+ data = unpacked_cookie_data(req)
102
130
  data = persistent_session_id!(data)
103
131
  [data["session_id"], data]
104
132
  end
105
133
 
106
- def extract_session_id(env)
107
- unpacked_cookie_data(env)["session_id"]
134
+ def extract_session_id(request)
135
+ unpacked_cookie_data(request)["session_id"]
108
136
  end
109
137
 
110
- def unpacked_cookie_data(env)
111
- env["rack.session.unpacked_cookie_data"] ||= begin
112
- request = Rack::Request.new(env)
138
+ def unpacked_cookie_data(request)
139
+ request.fetch_header(RACK_SESSION_UNPACKED_COOKIE_DATA) do |k|
113
140
  session_data = request.cookies[@key]
114
141
 
115
142
  if @secrets.size > 0 && session_data
116
- session_data, digest = session_data.split("--")
117
-
118
- if session_data && digest
119
- ok = @secrets.any? do |secret|
120
- secret && Rack::Utils.secure_compare(digest, generate_hmac(session_data, secret))
121
- end
122
- end
123
-
124
- session_data = nil unless ok
143
+ session_data, _, digest = session_data.rpartition('--')
144
+ session_data = nil unless digest_match?(session_data, digest)
125
145
  end
126
146
 
127
- coder.decode(session_data) || {}
147
+ request.set_header(k, coder.decode(session_data) || {})
128
148
  end
129
149
  end
130
150
 
131
- def persistent_session_id!(data, sid=nil)
151
+ def persistent_session_id!(data, sid = nil)
132
152
  data ||= {}
133
153
  data["session_id"] ||= sid || generate_sid
134
154
  data
135
155
  end
136
156
 
137
- # Overwrite set cookie to bypass content equality and always stream the cookie.
157
+ class SessionId < DelegateClass(Session::SessionId)
158
+ attr_reader :cookie_value
138
159
 
139
- def set_cookie(env, headers, cookie)
140
- Utils.set_cookie_header!(headers, @key, cookie)
160
+ def initialize(session_id, cookie_value)
161
+ super(session_id)
162
+ @cookie_value = cookie_value
163
+ end
141
164
  end
142
165
 
143
- def set_session(env, session_id, session, options)
166
+ def write_session(req, session_id, session, options)
144
167
  session = session.merge("session_id" => session_id)
145
168
  session_data = coder.encode(session)
146
169
 
147
170
  if @secrets.first
148
- session_data = "#{session_data}--#{generate_hmac(session_data, @secrets.first)}"
171
+ session_data << "--#{generate_hmac(session_data, @secrets.first)}"
149
172
  end
150
173
 
151
174
  if session_data.size > (4096 - @key.size)
152
- env["rack.errors"].puts("Warning! Rack::Session::Cookie data size exceeds 4K.")
175
+ req.get_header(RACK_ERRORS).puts("Warning! Rack::Session::Cookie data size exceeds 4K.")
153
176
  nil
154
177
  else
155
- session_data
178
+ SessionId.new(session_id, session_data)
156
179
  end
157
180
  end
158
181
 
159
- def destroy_session(env, session_id, options)
182
+ def delete_session(req, session_id, options)
160
183
  # Nothing to do here, data is in the client
161
184
  generate_sid unless options[:drop]
162
185
  end
163
186
 
187
+ def digest_match?(data, digest)
188
+ return unless data && digest
189
+ @secrets.any? do |secret|
190
+ Rack::Utils.secure_compare(digest, generate_hmac(data, secret))
191
+ end
192
+ end
193
+
164
194
  def generate_hmac(data, secret)
165
- OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, secret, data)
195
+ OpenSSL::HMAC.hexdigest(@hmac.new, secret, data)
196
+ end
197
+
198
+ def secure?(options)
199
+ @secrets.size >= 1 ||
200
+ (options[:coder] && options[:let_coder_handle_secure_encoding])
166
201
  end
167
202
 
168
203
  end
@@ -1,93 +1,10 @@
1
- # AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
1
+ # frozen_string_literal: true
2
2
 
3
- require 'rack/session/abstract/id'
4
- require 'memcache'
3
+ require 'rack/session/dalli'
5
4
 
6
5
  module Rack
7
6
  module Session
8
- # Rack::Session::Memcache provides simple cookie based session management.
9
- # Session data is stored in memcached. The corresponding session key is
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?{|c| c.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, [nil, {}]) 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, false) 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, default=nil)
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
- default
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
@@ -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
  # THANKS:
3
5
  # apeiros, for session id generation, expiry setup, and threadiness
@@ -24,11 +26,11 @@ module Rack
24
26
  # )
25
27
  # Rack::Handler::WEBrick.run sessioned
26
28
 
27
- class Pool < Abstract::ID
29
+ class Pool < Abstract::PersistedSecure
28
30
  attr_reader :mutex, :pool
29
- DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge :drop => false
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,43 +39,47 @@ 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
- def get_session(env, sid)
45
- with_lock(env, [nil, {}]) do
46
- unless sid and session = @pool[sid]
46
+ def find_session(req, sid)
47
+ with_lock(req) do
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
52
54
  end
53
55
 
54
- def set_session(env, session_id, new_session, options)
55
- with_lock(env, false) do
56
- @pool.store session_id, new_session
56
+ def write_session(req, session_id, new_session, options)
57
+ with_lock(req) do
58
+ @pool.store session_id.private_id, new_session
57
59
  session_id
58
60
  end
59
61
  end
60
62
 
61
- def destroy_session(env, session_id, options)
62
- with_lock(env) do
63
- @pool.delete(session_id)
63
+ def delete_session(req, session_id, options)
64
+ with_lock(req) do
65
+ @pool.delete(session_id.public_id)
66
+ @pool.delete(session_id.private_id)
64
67
  generate_sid unless options[:drop]
65
68
  end
66
69
  end
67
70
 
68
- def with_lock(env, default=nil)
69
- @mutex.lock if env['rack.multithread']
71
+ def with_lock(req)
72
+ @mutex.lock if req.multithread?
70
73
  yield
71
- rescue
72
- default
73
74
  ensure
74
75
  @mutex.unlock if @mutex.locked?
75
76
  end
76
77
 
78
+ private
79
+
80
+ def get_session_with_fallback(sid)
81
+ @pool[sid.private_id] || @pool[sid.public_id]
82
+ end
77
83
  end
78
84
  end
79
85
  end