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.

Files changed (187) hide show
  1. checksums.yaml +4 -4
  2. data/{HISTORY.md → CHANGELOG.md} +220 -155
  3. data/{COPYING → MIT-LICENSE} +4 -2
  4. data/README.rdoc +77 -119
  5. data/Rakefile +25 -18
  6. data/SPEC +3 -4
  7. data/bin/rackup +1 -0
  8. data/example/lobster.ru +2 -0
  9. data/example/protectedlobster.rb +3 -1
  10. data/example/protectedlobster.ru +2 -0
  11. data/lib/rack.rb +63 -60
  12. data/lib/rack/auth/abstract/handler.rb +3 -1
  13. data/lib/rack/auth/abstract/request.rb +2 -0
  14. data/lib/rack/auth/basic.rb +4 -1
  15. data/lib/rack/auth/digest/md5.rb +9 -7
  16. data/lib/rack/auth/digest/nonce.rb +6 -3
  17. data/lib/rack/auth/digest/params.rb +4 -2
  18. data/lib/rack/auth/digest/request.rb +2 -0
  19. data/lib/rack/body_proxy.rb +3 -6
  20. data/lib/rack/builder.rb +38 -15
  21. data/lib/rack/cascade.rb +6 -5
  22. data/lib/rack/chunked.rb +29 -6
  23. data/lib/rack/common_logger.rb +9 -8
  24. data/lib/rack/conditional_get.rb +3 -1
  25. data/lib/rack/config.rb +2 -0
  26. data/lib/rack/content_length.rb +3 -1
  27. data/lib/rack/content_type.rb +3 -1
  28. data/lib/rack/core_ext/regexp.rb +14 -0
  29. data/lib/rack/deflater.rb +28 -17
  30. data/lib/rack/directory.rb +17 -14
  31. data/lib/rack/etag.rb +3 -1
  32. data/lib/rack/events.rb +5 -3
  33. data/lib/rack/file.rb +5 -173
  34. data/lib/rack/files.rb +178 -0
  35. data/lib/rack/handler.rb +7 -2
  36. data/lib/rack/handler/cgi.rb +3 -1
  37. data/lib/rack/handler/fastcgi.rb +4 -2
  38. data/lib/rack/handler/lsws.rb +3 -1
  39. data/lib/rack/handler/scgi.rb +9 -6
  40. data/lib/rack/handler/thin.rb +3 -1
  41. data/lib/rack/handler/webrick.rb +4 -2
  42. data/lib/rack/head.rb +2 -0
  43. data/lib/rack/lint.rb +14 -11
  44. data/lib/rack/lobster.rb +7 -5
  45. data/lib/rack/lock.rb +2 -0
  46. data/lib/rack/logger.rb +2 -0
  47. data/lib/rack/media_type.rb +10 -5
  48. data/lib/rack/method_override.rb +9 -3
  49. data/lib/rack/mime.rb +9 -1
  50. data/lib/rack/mock.rb +74 -15
  51. data/lib/rack/multipart.rb +5 -3
  52. data/lib/rack/multipart/generator.rb +6 -7
  53. data/lib/rack/multipart/parser.rb +54 -51
  54. data/lib/rack/multipart/uploaded_file.rb +2 -0
  55. data/lib/rack/null_logger.rb +2 -0
  56. data/lib/rack/query_parser.rb +51 -25
  57. data/lib/rack/recursive.rb +7 -5
  58. data/lib/rack/reloader.rb +10 -4
  59. data/lib/rack/request.rb +89 -23
  60. data/lib/rack/response.rb +71 -31
  61. data/lib/rack/rewindable_input.rb +4 -2
  62. data/lib/rack/runtime.rb +4 -2
  63. data/lib/rack/sendfile.rb +15 -8
  64. data/lib/rack/server.rb +88 -16
  65. data/lib/rack/session/abstract/id.rb +104 -21
  66. data/lib/rack/session/cookie.rb +21 -11
  67. data/lib/rack/session/memcache.rb +4 -87
  68. data/lib/rack/session/pool.rb +17 -8
  69. data/lib/rack/show_exceptions.rb +16 -10
  70. data/lib/rack/show_status.rb +4 -2
  71. data/lib/rack/static.rb +15 -10
  72. data/lib/rack/tempfile_reaper.rb +2 -0
  73. data/lib/rack/urlmap.rb +11 -2
  74. data/lib/rack/utils.rb +55 -70
  75. data/rack.gemspec +19 -9
  76. metadata +32 -173
  77. data/test/builder/an_underscore_app.rb +0 -5
  78. data/test/builder/anything.rb +0 -5
  79. data/test/builder/comment.ru +0 -4
  80. data/test/builder/end.ru +0 -5
  81. data/test/builder/line.ru +0 -1
  82. data/test/builder/options.ru +0 -2
  83. data/test/cgi/assets/folder/test.js +0 -1
  84. data/test/cgi/assets/fonts/font.eot +0 -1
  85. data/test/cgi/assets/images/image.png +0 -1
  86. data/test/cgi/assets/index.html +0 -1
  87. data/test/cgi/assets/javascripts/app.js +0 -1
  88. data/test/cgi/assets/stylesheets/app.css +0 -1
  89. data/test/cgi/lighttpd.conf +0 -26
  90. data/test/cgi/rackup_stub.rb +0 -6
  91. data/test/cgi/sample_rackup.ru +0 -5
  92. data/test/cgi/test +0 -9
  93. data/test/cgi/test+directory/test+file +0 -1
  94. data/test/cgi/test.fcgi +0 -9
  95. data/test/cgi/test.gz +0 -0
  96. data/test/cgi/test.ru +0 -5
  97. data/test/gemloader.rb +0 -10
  98. data/test/helper.rb +0 -34
  99. data/test/multipart/bad_robots +0 -259
  100. data/test/multipart/binary +0 -0
  101. data/test/multipart/content_type_and_no_filename +0 -6
  102. data/test/multipart/empty +0 -10
  103. data/test/multipart/fail_16384_nofile +0 -814
  104. data/test/multipart/file1.txt +0 -1
  105. data/test/multipart/filename_and_modification_param +0 -7
  106. data/test/multipart/filename_and_no_name +0 -6
  107. data/test/multipart/filename_with_encoded_words +0 -7
  108. data/test/multipart/filename_with_escaped_quotes +0 -6
  109. data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
  110. data/test/multipart/filename_with_null_byte +0 -7
  111. data/test/multipart/filename_with_percent_escaped_quotes +0 -6
  112. data/test/multipart/filename_with_single_quote +0 -7
  113. data/test/multipart/filename_with_unescaped_percentages +0 -6
  114. data/test/multipart/filename_with_unescaped_percentages2 +0 -6
  115. data/test/multipart/filename_with_unescaped_percentages3 +0 -6
  116. data/test/multipart/filename_with_unescaped_quotes +0 -6
  117. data/test/multipart/ie +0 -6
  118. data/test/multipart/invalid_character +0 -6
  119. data/test/multipart/mixed_files +0 -21
  120. data/test/multipart/nested +0 -10
  121. data/test/multipart/none +0 -9
  122. data/test/multipart/quoted +0 -15
  123. data/test/multipart/rack-logo.png +0 -0
  124. data/test/multipart/semicolon +0 -6
  125. data/test/multipart/text +0 -15
  126. data/test/multipart/three_files_three_fields +0 -31
  127. data/test/multipart/unity3d_wwwform +0 -11
  128. data/test/multipart/webkit +0 -32
  129. data/test/rackup/config.ru +0 -31
  130. data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
  131. data/test/spec_auth_basic.rb +0 -89
  132. data/test/spec_auth_digest.rb +0 -260
  133. data/test/spec_body_proxy.rb +0 -85
  134. data/test/spec_builder.rb +0 -233
  135. data/test/spec_cascade.rb +0 -63
  136. data/test/spec_cgi.rb +0 -84
  137. data/test/spec_chunked.rb +0 -103
  138. data/test/spec_common_logger.rb +0 -95
  139. data/test/spec_conditional_get.rb +0 -103
  140. data/test/spec_config.rb +0 -23
  141. data/test/spec_content_length.rb +0 -86
  142. data/test/spec_content_type.rb +0 -46
  143. data/test/spec_deflater.rb +0 -375
  144. data/test/spec_directory.rb +0 -148
  145. data/test/spec_etag.rb +0 -108
  146. data/test/spec_events.rb +0 -133
  147. data/test/spec_fastcgi.rb +0 -85
  148. data/test/spec_file.rb +0 -264
  149. data/test/spec_handler.rb +0 -57
  150. data/test/spec_head.rb +0 -46
  151. data/test/spec_lint.rb +0 -515
  152. data/test/spec_lobster.rb +0 -59
  153. data/test/spec_lock.rb +0 -204
  154. data/test/spec_logger.rb +0 -24
  155. data/test/spec_media_type.rb +0 -42
  156. data/test/spec_method_override.rb +0 -96
  157. data/test/spec_mime.rb +0 -51
  158. data/test/spec_mock.rb +0 -359
  159. data/test/spec_multipart.rb +0 -722
  160. data/test/spec_null_logger.rb +0 -21
  161. data/test/spec_recursive.rb +0 -75
  162. data/test/spec_request.rb +0 -1393
  163. data/test/spec_response.rb +0 -510
  164. data/test/spec_rewindable_input.rb +0 -128
  165. data/test/spec_runtime.rb +0 -50
  166. data/test/spec_sendfile.rb +0 -125
  167. data/test/spec_server.rb +0 -193
  168. data/test/spec_session_abstract_id.rb +0 -31
  169. data/test/spec_session_abstract_session_hash.rb +0 -45
  170. data/test/spec_session_cookie.rb +0 -442
  171. data/test/spec_session_memcache.rb +0 -320
  172. data/test/spec_session_pool.rb +0 -210
  173. data/test/spec_show_exceptions.rb +0 -80
  174. data/test/spec_show_status.rb +0 -104
  175. data/test/spec_static.rb +0 -184
  176. data/test/spec_tempfile_reaper.rb +0 -64
  177. data/test/spec_thin.rb +0 -96
  178. data/test/spec_urlmap.rb +0 -237
  179. data/test/spec_utils.rb +0 -742
  180. data/test/spec_version.rb +0 -11
  181. data/test/spec_webrick.rb +0 -206
  182. data/test/static/another/index.html +0 -1
  183. data/test/static/foo.html +0 -1
  184. data/test/static/index.html +0 -1
  185. data/test/testrequest.rb +0 -78
  186. data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
  187. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'scgi'
2
4
  require 'stringio'
3
5
  require 'rack/content_length'
@@ -8,12 +10,12 @@ module Rack
8
10
  class SCGI < ::SCGI::Processor
9
11
  attr_accessor :app
10
12
 
11
- def self.run(app, options=nil)
13
+ def self.run(app, options = nil)
12
14
  options[:Socket] = UNIXServer.new(options[:File]) if options[:File]
13
- new(options.merge(:app=>app,
14
- :host=>options[:Host],
15
- :port=>options[:Port],
16
- :socket=>options[:Socket])).listen
15
+ new(options.merge(app: app,
16
+ host: options[:Host],
17
+ port: options[:Port],
18
+ socket: options[:Socket])).listen
17
19
  end
18
20
 
19
21
  def self.valid_options
@@ -41,7 +43,8 @@ module Rack
41
43
  env[QUERY_STRING] ||= ""
42
44
  env[SCRIPT_NAME] = ""
43
45
 
44
- rack_input = StringIO.new(input_body, encoding: Encoding::BINARY)
46
+ rack_input = StringIO.new(input_body)
47
+ rack_input.set_encoding(Encoding::BINARY)
45
48
 
46
49
  env.update(
47
50
  RACK_VERSION => Rack::VERSION,
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "thin"
2
4
  require "thin/server"
3
5
  require "thin/logging"
@@ -8,7 +10,7 @@ require "rack/chunked"
8
10
  module Rack
9
11
  module Handler
10
12
  class Thin
11
- def self.run(app, options={})
13
+ def self.run(app, options = {})
12
14
  environment = ENV['RACK_ENV'] || 'development'
13
15
  default_host = environment == 'development' ? 'localhost' : '0.0.0.0'
14
16
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'webrick'
2
4
  require 'stringio'
3
5
  require 'rack/content_length'
@@ -22,7 +24,7 @@ end
22
24
  module Rack
23
25
  module Handler
24
26
  class WEBrick < ::WEBrick::HTTPServlet::AbstractServlet
25
- def self.run(app, options={})
27
+ def self.run(app, options = {})
26
28
  environment = ENV['RACK_ENV'] || 'development'
27
29
  default_host = environment == 'development' ? 'localhost' : nil
28
30
 
@@ -79,7 +81,7 @@ module Rack
79
81
  env[QUERY_STRING] ||= ""
80
82
  unless env[PATH_INFO] == ""
81
83
  path, n = req.request_uri.path, env[SCRIPT_NAME].length
82
- env[PATH_INFO] = path[n, path.length-n]
84
+ env[PATH_INFO] = path[n, path.length - n]
83
85
  end
84
86
  env[REQUEST_PATH] ||= [env[SCRIPT_NAME], env[PATH_INFO]].join
85
87
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/body_proxy'
2
4
 
3
5
  module Rack
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/utils'
2
4
  require 'forwardable'
3
5
 
@@ -33,7 +35,7 @@ module Rack
33
35
 
34
36
  ## A Rack application is a Ruby object (not a class) that
35
37
  ## responds to +call+.
36
- def call(env=nil)
38
+ def call(env = nil)
37
39
  dup._call(env)
38
40
  end
39
41
 
@@ -123,9 +125,8 @@ module Rack
123
125
  ## the presence or absence of the
124
126
  ## appropriate HTTP header in the
125
127
  ## request. See
126
- ## <a href="https://tools.ietf.org/html/rfc3875#section-4.1.18">
127
- ## RFC3875 section 4.1.18</a> for
128
- ## specific behavior.
128
+ ## {RFC3875 section 4.1.18}[https://tools.ietf.org/html/rfc3875#section-4.1.18]
129
+ ## for specific behavior.
129
130
 
130
131
  ## In addition to this, the Rack environment must include these
131
132
  ## Rack-specific variables:
@@ -263,7 +264,7 @@ module Rack
263
264
  ## <tt>HTTP_CONTENT_TYPE</tt> or <tt>HTTP_CONTENT_LENGTH</tt>
264
265
  ## (use the versions without <tt>HTTP_</tt>).
265
266
  %w[HTTP_CONTENT_TYPE HTTP_CONTENT_LENGTH].each { |header|
266
- assert("env contains #{header}, must use #{header[5,-1]}") {
267
+ assert("env contains #{header}, must use #{header[5, -1]}") {
267
268
  not env.include? header
268
269
  }
269
270
  }
@@ -626,15 +627,17 @@ module Rack
626
627
  assert("headers object should respond to #each, but doesn't (got #{header.class} as headers)") {
627
628
  header.respond_to? :each
628
629
  }
629
- header.each { |key, value|
630
- ## Special headers starting "rack." are for communicating with the
631
- ## server, and must not be sent back to the client.
632
- next if key =~ /^rack\..+$/
633
630
 
631
+ header.each { |key, value|
634
632
  ## The header keys must be Strings.
635
633
  assert("header key must be a string, was #{key.class}") {
636
634
  key.kind_of? String
637
635
  }
636
+
637
+ ## Special headers starting "rack." are for communicating with the
638
+ ## server, and must not be sent back to the client.
639
+ next if key =~ /^rack\..+$/
640
+
638
641
  ## The header must not contain a +Status+ key.
639
642
  assert("header must not contain Status") { key.downcase != "status" }
640
643
  ## The header must conform to RFC7230 token specification, i.e. cannot
@@ -662,7 +665,7 @@ module Rack
662
665
  ## 204 or 304.
663
666
  if key.downcase == "content-type"
664
667
  assert("Content-Type header found in #{status} response, not allowed") {
665
- not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i
668
+ not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.key? status.to_i
666
669
  }
667
670
  return
668
671
  end
@@ -676,7 +679,7 @@ module Rack
676
679
  ## There must not be a <tt>Content-Length</tt> header when the
677
680
  ## +Status+ is 1xx, 204 or 304.
678
681
  assert("Content-Length header found in #{status} response, not allowed") {
679
- not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i
682
+ not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.key? status.to_i
680
683
  }
681
684
  @content_length = value
682
685
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'zlib'
2
4
 
3
5
  require 'rack/request'
@@ -25,8 +27,8 @@ module Rack
25
27
  content = ["<title>Lobstericious!</title>",
26
28
  "<pre>", lobster, "</pre>",
27
29
  "<a href='#{href}'>flip!</a>"]
28
- length = content.inject(0) { |a,e| a+e.size }.to_s
29
- [200, {CONTENT_TYPE => "text/html", CONTENT_LENGTH => length}, content]
30
+ length = content.inject(0) { |a, e| a + e.size }.to_s
31
+ [200, { CONTENT_TYPE => "text/html", CONTENT_LENGTH => length }, content]
30
32
  }
31
33
 
32
34
  def call(env)
@@ -37,8 +39,8 @@ module Rack
37
39
  gsub('\\', 'TEMP').
38
40
  gsub('/', '\\').
39
41
  gsub('TEMP', '/').
40
- gsub('{','}').
41
- gsub('(',')')
42
+ gsub('{', '}').
43
+ gsub('(', ')')
42
44
  end.join("\n")
43
45
  href = "?flip=right"
44
46
  elsif req.GET["flip"] == "crash"
@@ -65,6 +67,6 @@ if $0 == __FILE__
65
67
  require 'rack'
66
68
  require 'rack/show_exceptions'
67
69
  Rack::Server.start(
68
- :app => Rack::ShowExceptions.new(Rack::Lint.new(Rack::Lobster.new)), :Port => 9292
70
+ app: Rack::ShowExceptions.new(Rack::Lint.new(Rack::Lobster.new)), Port: 9292
69
71
  )
70
72
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'thread'
2
4
  require 'rack/body_proxy'
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'logger'
2
4
 
3
5
  module Rack
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
4
  # Rack::MediaType parse media type and parameters out of content_type string
3
5
 
@@ -13,7 +15,7 @@ module Rack
13
15
  # http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7
14
16
  def type(content_type)
15
17
  return nil unless content_type
16
- content_type.split(SPLIT_PATTERN, 2).first.downcase
18
+ content_type.split(SPLIT_PATTERN, 2).first.tap &:downcase!
17
19
  end
18
20
 
19
21
  # The media type parameters provided in CONTENT_TYPE as a Hash, or
@@ -23,15 +25,18 @@ module Rack
23
25
  # { 'charset' => 'utf-8' }
24
26
  def params(content_type)
25
27
  return {} if content_type.nil?
26
- Hash[*content_type.split(SPLIT_PATTERN)[1..-1].
27
- collect { |s| s.split('=', 2) }.
28
- map { |k,v| [k.downcase, strip_doublequotes(v)] }.flatten]
28
+
29
+ content_type.split(SPLIT_PATTERN)[1..-1].each_with_object({}) do |s, hsh|
30
+ k, v = s.split('=', 2)
31
+
32
+ hsh[k.tap(&:downcase!)] = strip_doublequotes(v)
33
+ end
29
34
  end
30
35
 
31
36
  private
32
37
 
33
38
  def strip_doublequotes(str)
34
- (str[0] == ?" && str[-1] == ?") ? str[1..-2] : str
39
+ (str.start_with?('"') && str.end_with?('"')) ? str[1..-2] : str
35
40
  end
36
41
  end
37
42
  end
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
4
  class MethodOverride
3
5
  HTTP_METHODS = %w[GET HEAD PUT POST DELETE OPTIONS PATCH LINK UNLINK]
4
6
 
5
- METHOD_OVERRIDE_PARAM_KEY = "_method".freeze
6
- HTTP_METHOD_OVERRIDE_HEADER = "HTTP_X_HTTP_METHOD_OVERRIDE".freeze
7
+ METHOD_OVERRIDE_PARAM_KEY = "_method"
8
+ HTTP_METHOD_OVERRIDE_HEADER = "HTTP_X_HTTP_METHOD_OVERRIDE"
7
9
  ALLOWED_METHODS = %w[POST]
8
10
 
9
11
  def initialize(app)
@@ -26,7 +28,11 @@ module Rack
26
28
  req = Request.new(env)
27
29
  method = method_override_param(req) ||
28
30
  env[HTTP_METHOD_OVERRIDE_HEADER]
29
- method.to_s.upcase
31
+ begin
32
+ method.to_s.upcase
33
+ rescue ArgumentError
34
+ env[RACK_ERRORS].puts "Invalid string for method"
35
+ end
30
36
  end
31
37
 
32
38
  private
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
4
  module Mime
3
5
  # Returns String with mime type if found, otherwise use +fallback+.
@@ -13,7 +15,7 @@ module Rack
13
15
  # This is a shortcut for:
14
16
  # Rack::Mime::MIME_TYPES.fetch('.foo', 'application/octet-stream')
15
17
 
16
- def mime_type(ext, fallback='application/octet-stream')
18
+ def mime_type(ext, fallback = 'application/octet-stream')
17
19
  MIME_TYPES.fetch(ext.to_s.downcase, fallback)
18
20
  end
19
21
  module_function :mime_type
@@ -306,6 +308,7 @@ module Rack
306
308
  ".lvp" => "audio/vnd.lucent.voice",
307
309
  ".lwp" => "application/vnd.lotus-wordpro",
308
310
  ".m3u" => "audio/x-mpegurl",
311
+ ".m3u8" => "application/x-mpegurl",
309
312
  ".m4a" => "audio/mp4a-latm",
310
313
  ".m4v" => "video/mp4",
311
314
  ".ma" => "application/mathematica",
@@ -343,6 +346,7 @@ module Rack
343
346
  ".mp4s" => "application/mp4",
344
347
  ".mp4v" => "video/mp4",
345
348
  ".mpc" => "application/vnd.mophun.certificate",
349
+ ".mpd" => "application/dash+xml",
346
350
  ".mpeg" => "video/mpeg",
347
351
  ".mpg" => "video/mpeg",
348
352
  ".mpga" => "audio/mpeg",
@@ -542,6 +546,7 @@ module Rack
542
546
  ".spp" => "application/scvp-vp-response",
543
547
  ".spq" => "application/scvp-vp-request",
544
548
  ".src" => "application/x-wais-source",
549
+ ".srt" => "text/srt",
545
550
  ".srx" => "application/sparql-results+xml",
546
551
  ".sse" => "application/vnd.kodak-descriptor",
547
552
  ".ssf" => "application/vnd.epson.ssf",
@@ -576,6 +581,7 @@ module Rack
576
581
  ".tr" => "text/troff",
577
582
  ".tra" => "application/vnd.trueapp",
578
583
  ".trm" => "application/x-msterminal",
584
+ ".ts" => "video/mp2t",
579
585
  ".tsv" => "text/tab-separated-values",
580
586
  ".ttf" => "application/octet-stream",
581
587
  ".twd" => "application/vnd.simtech-mindmapper",
@@ -600,9 +606,11 @@ module Rack
600
606
  ".vrml" => "model/vrml",
601
607
  ".vsd" => "application/vnd.visio",
602
608
  ".vsf" => "application/vnd.vsf",
609
+ ".vtt" => "text/vtt",
603
610
  ".vtu" => "model/vnd.vtu",
604
611
  ".vxml" => "application/voicexml+xml",
605
612
  ".war" => "application/java-archive",
613
+ ".wasm" => "application/wasm",
606
614
  ".wav" => "audio/x-wav",
607
615
  ".wax" => "audio/x-ms-wax",
608
616
  ".wbmp" => "image/vnd.wap.wbmp",
@@ -1,9 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'uri'
2
4
  require 'stringio'
3
5
  require 'rack'
4
6
  require 'rack/lint'
5
7
  require 'rack/utils'
6
8
  require 'rack/response'
9
+ require 'cgi/cookie'
7
10
 
8
11
  module Rack
9
12
  # Rack::MockRequest helps testing your Rack application without
@@ -53,16 +56,16 @@ module Rack
53
56
  @app = app
54
57
  end
55
58
 
56
- def get(uri, opts={}) request(GET, uri, opts) end
57
- def post(uri, opts={}) request(POST, uri, opts) end
58
- def put(uri, opts={}) request(PUT, uri, opts) end
59
- def patch(uri, opts={}) request(PATCH, uri, opts) end
60
- def delete(uri, opts={}) request(DELETE, uri, opts) end
61
- def head(uri, opts={}) request(HEAD, uri, opts) end
62
- def options(uri, opts={}) request(OPTIONS, uri, opts) end
59
+ def get(uri, opts = {}) request(GET, uri, opts) end
60
+ def post(uri, opts = {}) request(POST, uri, opts) end
61
+ def put(uri, opts = {}) request(PUT, uri, opts) end
62
+ def patch(uri, opts = {}) request(PATCH, uri, opts) end
63
+ def delete(uri, opts = {}) request(DELETE, uri, opts) end
64
+ def head(uri, opts = {}) request(HEAD, uri, opts) end
65
+ def options(uri, opts = {}) request(OPTIONS, uri, opts) end
63
66
 
64
- def request(method=GET, uri="", opts={})
65
- env = self.class.env_for(uri, opts.merge(:method => method))
67
+ def request(method = GET, uri = "", opts = {})
68
+ env = self.class.env_for(uri, opts.merge(method: method))
66
69
 
67
70
  if opts[:lint]
68
71
  app = Rack::Lint.new(@app)
@@ -71,7 +74,7 @@ module Rack
71
74
  end
72
75
 
73
76
  errors = env[RACK_ERRORS]
74
- status, headers, body = app.call(env)
77
+ status, headers, body = app.call(env)
75
78
  MockResponse.new(status, headers, body, errors)
76
79
  ensure
77
80
  body.close if body.respond_to?(:close)
@@ -85,7 +88,7 @@ module Rack
85
88
  end
86
89
 
87
90
  # Return the Rack environment used for a request to +uri+.
88
- def self.env_for(uri="", opts={})
91
+ def self.env_for(uri = "", opts = {})
89
92
  uri = parse_uri_rfc2396(uri)
90
93
  uri.path = "/#{uri.path}" unless uri.path[0] == ?/
91
94
 
@@ -139,7 +142,7 @@ module Rack
139
142
  rack_input.set_encoding(Encoding::BINARY)
140
143
  env[RACK_INPUT] = rack_input
141
144
 
142
- env["CONTENT_LENGTH"] ||= env[RACK_INPUT].length.to_s
145
+ env["CONTENT_LENGTH"] ||= env[RACK_INPUT].size.to_s if env[RACK_INPUT].respond_to?(:size)
143
146
 
144
147
  opts.each { |field, value|
145
148
  env[field] = value if String === field
@@ -155,16 +158,19 @@ module Rack
155
158
 
156
159
  class MockResponse < Rack::Response
157
160
  # Headers
158
- attr_reader :original_headers
161
+ attr_reader :original_headers, :cookies
159
162
 
160
163
  # Errors
161
164
  attr_accessor :errors
162
165
 
163
- def initialize(status, headers, body, errors=StringIO.new(""))
166
+ def initialize(status, headers, body, errors = StringIO.new(""))
164
167
  @original_headers = headers
165
168
  @errors = errors.string if errors.respond_to?(:string)
169
+ @cookies = parse_cookies_from_header
166
170
 
167
171
  super(body, status, headers)
172
+
173
+ buffered_body!
168
174
  end
169
175
 
170
176
  def =~(other)
@@ -186,11 +192,64 @@ module Rack
186
192
  # ...
187
193
  # res.body.should == "foo!"
188
194
  # end
189
- super.join
195
+ buffer = String.new
196
+
197
+ super.each do |chunk|
198
+ buffer << chunk
199
+ end
200
+
201
+ return buffer
190
202
  end
191
203
 
192
204
  def empty?
193
205
  [201, 204, 304].include? status
194
206
  end
207
+
208
+ def cookie(name)
209
+ cookies.fetch(name, nil)
210
+ end
211
+
212
+ private
213
+
214
+ def parse_cookies_from_header
215
+ cookies = Hash.new
216
+ if original_headers.has_key? 'Set-Cookie'
217
+ set_cookie_header = original_headers.fetch('Set-Cookie')
218
+ set_cookie_header.split("\n").each do |cookie|
219
+ cookie_name, cookie_filling = cookie.split('=', 2)
220
+ cookie_attributes = identify_cookie_attributes cookie_filling
221
+ parsed_cookie = CGI::Cookie.new(
222
+ 'name' => cookie_name.strip,
223
+ 'value' => cookie_attributes.fetch('value'),
224
+ 'path' => cookie_attributes.fetch('path', nil),
225
+ 'domain' => cookie_attributes.fetch('domain', nil),
226
+ 'expires' => cookie_attributes.fetch('expires', nil),
227
+ 'secure' => cookie_attributes.fetch('secure', false)
228
+ )
229
+ cookies.store(cookie_name, parsed_cookie)
230
+ end
231
+ end
232
+ cookies
233
+ end
234
+
235
+ def identify_cookie_attributes(cookie_filling)
236
+ cookie_bits = cookie_filling.split(';')
237
+ cookie_attributes = Hash.new
238
+ cookie_attributes.store('value', cookie_bits[0].strip)
239
+ cookie_bits.each do |bit|
240
+ if bit.include? '='
241
+ cookie_attribute, attribute_value = bit.split('=')
242
+ cookie_attributes.store(cookie_attribute.strip, attribute_value.strip)
243
+ if cookie_attribute.include? 'max-age'
244
+ cookie_attributes.store('expires', Time.now + attribute_value.strip.to_i)
245
+ end
246
+ end
247
+ if bit.include? 'secure'
248
+ cookie_attributes.store('secure', true)
249
+ end
250
+ end
251
+ cookie_attributes
252
+ end
253
+
195
254
  end
196
255
  end