rack 3.0.15 → 3.2.6

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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +368 -6
  3. data/CONTRIBUTING.md +11 -9
  4. data/README.md +103 -28
  5. data/SPEC.rdoc +206 -288
  6. data/lib/rack/auth/abstract/request.rb +2 -0
  7. data/lib/rack/auth/basic.rb +1 -2
  8. data/lib/rack/bad_request.rb +8 -0
  9. data/lib/rack/builder.rb +29 -10
  10. data/lib/rack/cascade.rb +0 -3
  11. data/lib/rack/conditional_get.rb +4 -3
  12. data/lib/rack/constants.rb +4 -0
  13. data/lib/rack/directory.rb +6 -3
  14. data/lib/rack/events.rb +21 -6
  15. data/lib/rack/files.rb +1 -1
  16. data/lib/rack/head.rb +2 -3
  17. data/lib/rack/headers.rb +86 -2
  18. data/lib/rack/lint.rb +482 -425
  19. data/lib/rack/media_type.rb +14 -10
  20. data/lib/rack/mime.rb +6 -5
  21. data/lib/rack/mock_request.rb +10 -15
  22. data/lib/rack/mock_response.rb +50 -20
  23. data/lib/rack/multipart/parser.rb +255 -76
  24. data/lib/rack/multipart/uploaded_file.rb +42 -5
  25. data/lib/rack/multipart.rb +34 -1
  26. data/lib/rack/query_parser.rb +86 -78
  27. data/lib/rack/request.rb +78 -65
  28. data/lib/rack/response.rb +28 -20
  29. data/lib/rack/rewindable_input.rb +4 -1
  30. data/lib/rack/sendfile.rb +51 -21
  31. data/lib/rack/show_exceptions.rb +10 -4
  32. data/lib/rack/show_status.rb +0 -2
  33. data/lib/rack/static.rb +7 -3
  34. data/lib/rack/utils.rb +175 -119
  35. data/lib/rack/version.rb +3 -20
  36. data/lib/rack.rb +1 -4
  37. metadata +6 -12
  38. data/lib/rack/auth/digest/md5.rb +0 -1
  39. data/lib/rack/auth/digest/nonce.rb +0 -1
  40. data/lib/rack/auth/digest/params.rb +0 -1
  41. data/lib/rack/auth/digest/request.rb +0 -1
  42. data/lib/rack/auth/digest.rb +0 -256
  43. data/lib/rack/chunked.rb +0 -120
  44. data/lib/rack/file.rb +0 -9
  45. data/lib/rack/logger.rb +0 -22
@@ -14,12 +14,11 @@ module Rack
14
14
  # For more information on the use of media types in HTTP, see:
15
15
  # http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7
16
16
  def type(content_type)
17
- return nil unless content_type
18
- if type = content_type.split(SPLIT_PATTERN, 2).first
19
- type.rstrip!
20
- type.downcase!
21
- type
22
- end
17
+ return nil unless content_type && !content_type.empty?
18
+ type = content_type.split(SPLIT_PATTERN, 2).first
19
+ type.rstrip!
20
+ type.downcase!
21
+ type
23
22
  end
24
23
 
25
24
  # The media type parameters provided in CONTENT_TYPE as a Hash, or
@@ -27,8 +26,13 @@ module Rack
27
26
  # provided. e.g., when the CONTENT_TYPE is "text/plain;charset=utf-8",
28
27
  # this method responds with the following Hash:
29
28
  # { 'charset' => 'utf-8' }
29
+ #
30
+ # This will pass back parameters with empty strings in the hash if they
31
+ # lack a value (e.g., "text/plain;charset=" will return { 'charset' => '' },
32
+ # and "text/plain;charset" will return { 'charset' => '' }, similarly to
33
+ # the query params parser (barring the latter case, which returns nil instead)).
30
34
  def params(content_type)
31
- return {} if content_type.nil?
35
+ return {} if content_type.nil? || content_type.empty?
32
36
 
33
37
  content_type.split(SPLIT_PATTERN)[1..-1].each_with_object({}) do |s, hsh|
34
38
  s.strip!
@@ -40,9 +44,9 @@ module Rack
40
44
 
41
45
  private
42
46
 
43
- def strip_doublequotes(str)
44
- (str.start_with?('"') && str.end_with?('"')) ? str[1..-2] : str
45
- end
47
+ def strip_doublequotes(str)
48
+ (str && str.start_with?('"') && str.end_with?('"')) ? str[1..-2] : str || ''
49
+ end
46
50
  end
47
51
  end
48
52
  end
data/lib/rack/mime.rb CHANGED
@@ -290,7 +290,7 @@ module Rack
290
290
  ".jpg" => "image/jpeg",
291
291
  ".jpgv" => "video/jpeg",
292
292
  ".jpm" => "video/jpm",
293
- ".js" => "application/javascript",
293
+ ".js" => "text/javascript",
294
294
  ".json" => "application/json",
295
295
  ".karbon" => "application/vnd.kde.karbon",
296
296
  ".kfo" => "application/vnd.kde.kformula",
@@ -338,6 +338,7 @@ module Rack
338
338
  ".mif" => "application/vnd.mif",
339
339
  ".mime" => "message/rfc822",
340
340
  ".mj2" => "video/mj2",
341
+ ".mjs" => "text/javascript",
341
342
  ".mlp" => "application/vnd.dolby.mlp",
342
343
  ".mmd" => "application/vnd.chipnuts.karaoke-mmd",
343
344
  ".mmf" => "application/vnd.smaf",
@@ -409,7 +410,7 @@ module Rack
409
410
  ".ogx" => "application/ogg",
410
411
  ".org" => "application/vnd.lotus-organizer",
411
412
  ".otc" => "application/vnd.oasis.opendocument.chart-template",
412
- ".otf" => "application/vnd.oasis.opendocument.formula-template",
413
+ ".otf" => "font/otf",
413
414
  ".otg" => "application/vnd.oasis.opendocument.graphics-template",
414
415
  ".oth" => "application/vnd.oasis.opendocument.text-web",
415
416
  ".oti" => "application/vnd.oasis.opendocument.image-template",
@@ -590,7 +591,7 @@ module Rack
590
591
  ".trm" => "application/x-msterminal",
591
592
  ".ts" => "video/mp2t",
592
593
  ".tsv" => "text/tab-separated-values",
593
- ".ttf" => "application/octet-stream",
594
+ ".ttf" => "font/ttf",
594
595
  ".twd" => "application/vnd.simtech-mindmapper",
595
596
  ".txd" => "application/vnd.genomatix.tuxedo",
596
597
  ".txf" => "application/vnd.mobius.txf",
@@ -636,8 +637,8 @@ module Rack
636
637
  ".wmv" => "video/x-ms-wmv",
637
638
  ".wmx" => "video/x-ms-wmx",
638
639
  ".wmz" => "application/x-ms-wmz",
639
- ".woff" => "application/font-woff",
640
- ".woff2" => "application/font-woff2",
640
+ ".woff" => "font/woff",
641
+ ".woff2" => "font/woff2",
641
642
  ".wpd" => "application/vnd.wordperfect",
642
643
  ".wpl" => "application/vnd.ms-wpl",
643
644
  ".wps" => "application/vnd.ms-works",
@@ -41,11 +41,6 @@ module Rack
41
41
  end
42
42
  end
43
43
 
44
- DEFAULT_ENV = {
45
- RACK_INPUT => StringIO.new,
46
- RACK_ERRORS => StringIO.new,
47
- }.freeze
48
-
49
44
  def initialize(app)
50
45
  @app = app
51
46
  end
@@ -104,7 +99,7 @@ module Rack
104
99
  uri = parse_uri_rfc2396(uri)
105
100
  uri.path = "/#{uri.path}" unless uri.path[0] == ?/
106
101
 
107
- env = DEFAULT_ENV.dup
102
+ env = {}
108
103
 
109
104
  env[REQUEST_METHOD] = (opts[:method] ? opts[:method].to_s.upcase : GET).b
110
105
  env[SERVER_NAME] = (uri.host || "example.org").b
@@ -144,20 +139,20 @@ module Rack
144
139
  end
145
140
  end
146
141
 
147
- opts[:input] ||= String.new
148
- if String === opts[:input]
149
- rack_input = StringIO.new(opts[:input])
150
- else
151
- rack_input = opts[:input]
142
+ rack_input = opts[:input]
143
+ if String === rack_input
144
+ rack_input = StringIO.new(rack_input)
152
145
  end
153
146
 
154
- rack_input.set_encoding(Encoding::BINARY)
155
- env[RACK_INPUT] = rack_input
147
+ if rack_input
148
+ rack_input.set_encoding(Encoding::BINARY) if rack_input.respond_to?(:set_encoding)
149
+ env[RACK_INPUT] = rack_input
156
150
 
157
- env["CONTENT_LENGTH"] ||= env[RACK_INPUT].size.to_s if env[RACK_INPUT].respond_to?(:size)
151
+ env["CONTENT_LENGTH"] ||= env[RACK_INPUT].size.to_s if env[RACK_INPUT].respond_to?(:size)
152
+ end
158
153
 
159
154
  opts.each { |field, value|
160
- env[field] = value if String === field
155
+ env[field] = value if String === field
161
156
  }
162
157
 
163
158
  env
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'cgi/cookie'
3
+ require 'stringio'
4
4
  require 'time'
5
5
 
6
6
  require_relative 'response'
@@ -11,6 +11,30 @@ module Rack
11
11
  # MockRequest.
12
12
 
13
13
  class MockResponse < Rack::Response
14
+ class Cookie
15
+ attr_reader :name, :value, :path, :domain, :expires, :secure
16
+
17
+ def initialize(args)
18
+ @name = args["name"]
19
+ @value = args["value"]
20
+ @path = args["path"]
21
+ @domain = args["domain"]
22
+ @expires = args["expires"]
23
+ @secure = args["secure"]
24
+ end
25
+
26
+ def method_missing(method_name, *args, &block)
27
+ @value.send(method_name, *args, &block)
28
+ end
29
+ # :nocov:
30
+ ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
31
+ # :nocov:
32
+
33
+ def respond_to_missing?(method_name, include_all = false)
34
+ @value.respond_to?(method_name, include_all) || super
35
+ end
36
+ end
37
+
14
38
  class << self
15
39
  alias [] new
16
40
  end
@@ -59,8 +83,16 @@ module Rack
59
83
  # end
60
84
  buffer = @buffered_body = String.new
61
85
 
62
- @body.each do |chunk|
63
- buffer << chunk
86
+ begin
87
+ if @body.respond_to?(:each)
88
+ @body.each do |chunk|
89
+ buffer << chunk
90
+ end
91
+ else
92
+ @body.call(StringIO.new(buffer))
93
+ end
94
+ ensure
95
+ @body.close if @body.respond_to?(:close)
64
96
  end
65
97
 
66
98
  return buffer
@@ -78,22 +110,20 @@ module Rack
78
110
 
79
111
  def parse_cookies_from_header
80
112
  cookies = Hash.new
81
- if headers.has_key? 'set-cookie'
82
- set_cookie_header = headers.fetch('set-cookie')
83
- Array(set_cookie_header).each do |header_value|
84
- header_value.split("\n").each do |cookie|
85
- cookie_name, cookie_filling = cookie.split('=', 2)
86
- cookie_attributes = identify_cookie_attributes cookie_filling
87
- parsed_cookie = CGI::Cookie.new(
88
- 'name' => cookie_name.strip,
89
- 'value' => cookie_attributes.fetch('value'),
90
- 'path' => cookie_attributes.fetch('path', nil),
91
- 'domain' => cookie_attributes.fetch('domain', nil),
92
- 'expires' => cookie_attributes.fetch('expires', nil),
93
- 'secure' => cookie_attributes.fetch('secure', false)
94
- )
95
- cookies.store(cookie_name, parsed_cookie)
96
- end
113
+ set_cookie_header = headers['set-cookie']
114
+ if set_cookie_header && !set_cookie_header.empty?
115
+ Array(set_cookie_header).each do |cookie|
116
+ cookie_name, cookie_filling = cookie.split('=', 2)
117
+ cookie_attributes = identify_cookie_attributes cookie_filling
118
+ parsed_cookie = Cookie.new(
119
+ 'name' => cookie_name.strip,
120
+ 'value' => cookie_attributes.fetch('value'),
121
+ 'path' => cookie_attributes.fetch('path', nil),
122
+ 'domain' => cookie_attributes.fetch('domain', nil),
123
+ 'expires' => cookie_attributes.fetch('expires', nil),
124
+ 'secure' => cookie_attributes.fetch('secure', false)
125
+ )
126
+ cookies.store(cookie_name, parsed_cookie)
97
127
  end
98
128
  end
99
129
  cookies
@@ -102,7 +132,7 @@ module Rack
102
132
  def identify_cookie_attributes(cookie_filling)
103
133
  cookie_bits = cookie_filling.split(';')
104
134
  cookie_attributes = Hash.new
105
- cookie_attributes.store('value', cookie_bits[0].strip)
135
+ cookie_attributes.store('value', Array(cookie_bits[0].strip))
106
136
  cookie_bits.drop(1).each do |bit|
107
137
  if bit.include? '='
108
138
  cookie_attribute, attribute_value = bit.split('=', 2)