rack 2.2.4 → 3.0.8

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 (87) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +223 -71
  3. data/CONTRIBUTING.md +53 -47
  4. data/MIT-LICENSE +1 -1
  5. data/README.md +309 -0
  6. data/SPEC.rdoc +183 -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 +0 -2
  10. data/lib/rack/auth/digest/md5.rb +1 -131
  11. data/lib/rack/auth/digest/nonce.rb +1 -54
  12. data/lib/rack/auth/digest/params.rb +1 -54
  13. data/lib/rack/auth/digest/request.rb +1 -43
  14. data/lib/rack/auth/digest.rb +256 -0
  15. data/lib/rack/body_proxy.rb +3 -1
  16. data/lib/rack/builder.rb +83 -63
  17. data/lib/rack/cascade.rb +2 -0
  18. data/lib/rack/chunked.rb +16 -13
  19. data/lib/rack/common_logger.rb +23 -18
  20. data/lib/rack/conditional_get.rb +18 -15
  21. data/lib/rack/constants.rb +64 -0
  22. data/lib/rack/content_length.rb +12 -16
  23. data/lib/rack/content_type.rb +8 -5
  24. data/lib/rack/deflater.rb +40 -26
  25. data/lib/rack/directory.rb +9 -3
  26. data/lib/rack/etag.rb +14 -23
  27. data/lib/rack/events.rb +4 -0
  28. data/lib/rack/file.rb +2 -0
  29. data/lib/rack/files.rb +15 -17
  30. data/lib/rack/head.rb +9 -8
  31. data/lib/rack/headers.rb +154 -0
  32. data/lib/rack/lint.rb +783 -682
  33. data/lib/rack/lock.rb +2 -5
  34. data/lib/rack/logger.rb +2 -0
  35. data/lib/rack/media_type.rb +1 -1
  36. data/lib/rack/method_override.rb +6 -2
  37. data/lib/rack/mime.rb +8 -0
  38. data/lib/rack/mock.rb +1 -271
  39. data/lib/rack/mock_request.rb +166 -0
  40. data/lib/rack/mock_response.rb +126 -0
  41. data/lib/rack/multipart/generator.rb +7 -5
  42. data/lib/rack/multipart/parser.rb +134 -65
  43. data/lib/rack/multipart/uploaded_file.rb +4 -0
  44. data/lib/rack/multipart.rb +20 -40
  45. data/lib/rack/null_logger.rb +9 -0
  46. data/lib/rack/query_parser.rb +78 -46
  47. data/lib/rack/recursive.rb +2 -0
  48. data/lib/rack/reloader.rb +0 -2
  49. data/lib/rack/request.rb +226 -108
  50. data/lib/rack/response.rb +136 -61
  51. data/lib/rack/rewindable_input.rb +24 -5
  52. data/lib/rack/runtime.rb +7 -6
  53. data/lib/rack/sendfile.rb +30 -25
  54. data/lib/rack/show_exceptions.rb +15 -2
  55. data/lib/rack/show_status.rb +17 -7
  56. data/lib/rack/static.rb +8 -8
  57. data/lib/rack/tempfile_reaper.rb +15 -4
  58. data/lib/rack/urlmap.rb +4 -2
  59. data/lib/rack/utils.rb +223 -185
  60. data/lib/rack/version.rb +9 -4
  61. data/lib/rack.rb +6 -76
  62. metadata +18 -38
  63. data/README.rdoc +0 -306
  64. data/Rakefile +0 -130
  65. data/bin/rackup +0 -5
  66. data/contrib/rack.png +0 -0
  67. data/contrib/rack.svg +0 -150
  68. data/contrib/rack_logo.svg +0 -164
  69. data/contrib/rdoc.css +0 -412
  70. data/example/lobster.ru +0 -6
  71. data/example/protectedlobster.rb +0 -16
  72. data/example/protectedlobster.ru +0 -10
  73. data/lib/rack/core_ext/regexp.rb +0 -14
  74. data/lib/rack/handler/cgi.rb +0 -59
  75. data/lib/rack/handler/fastcgi.rb +0 -100
  76. data/lib/rack/handler/lsws.rb +0 -61
  77. data/lib/rack/handler/scgi.rb +0 -71
  78. data/lib/rack/handler/thin.rb +0 -36
  79. data/lib/rack/handler/webrick.rb +0 -129
  80. data/lib/rack/handler.rb +0 -104
  81. data/lib/rack/lobster.rb +0 -70
  82. data/lib/rack/server.rb +0 -466
  83. data/lib/rack/session/abstract/id.rb +0 -523
  84. data/lib/rack/session/cookie.rb +0 -203
  85. data/lib/rack/session/memcache.rb +0 -10
  86. data/lib/rack/session/pool.rb +0 -85
  87. data/rack.gemspec +0 -46
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'uploaded_file'
4
+
3
5
  module Rack
4
6
  module Multipart
5
7
  class Generator
@@ -74,12 +76,12 @@ module Rack
74
76
 
75
77
  def content_for_tempfile(io, file, name)
76
78
  length = ::File.stat(file.path).size if file.path
77
- filename = "; filename=\"#{Utils.escape(file.original_filename)}\"" if file.original_filename
79
+ filename = "; filename=\"#{Utils.escape_path(file.original_filename)}\""
78
80
  <<-EOF
79
81
  --#{MULTIPART_BOUNDARY}\r
80
- Content-Disposition: form-data; name="#{name}"#{filename}\r
81
- Content-Type: #{file.content_type}\r
82
- #{"Content-Length: #{length}\r\n" if length}\r
82
+ content-disposition: form-data; name="#{name}"#{filename}\r
83
+ content-type: #{file.content_type}\r
84
+ #{"content-length: #{length}\r\n" if length}\r
83
85
  #{io.read}\r
84
86
  EOF
85
87
  end
@@ -87,7 +89,7 @@ EOF
87
89
  def content_for_other(file, name)
88
90
  <<-EOF
89
91
  --#{MULTIPART_BOUNDARY}\r
90
- Content-Disposition: form-data; name="#{name}"\r
92
+ content-disposition: form-data; name="#{name}"\r
91
93
  \r
92
94
  #{file}\r
93
95
  EOF
@@ -2,21 +2,54 @@
2
2
 
3
3
  require 'strscan'
4
4
 
5
+ require_relative '../utils'
6
+
5
7
  module Rack
6
8
  module Multipart
7
9
  class MultipartPartLimitError < Errno::EMFILE; end
8
10
 
9
- class Parser
10
- (require_relative '../core_ext/regexp'; using ::Rack::RegexpExtensions) if RUBY_VERSION < '2.4'
11
+ class MultipartTotalPartLimitError < StandardError; end
12
+
13
+ # Use specific error class when parsing multipart request
14
+ # that ends early.
15
+ class EmptyContentError < ::EOFError; end
16
+
17
+ # Base class for multipart exceptions that do not subclass from
18
+ # other exception classes for backwards compatibility.
19
+ class Error < StandardError; end
20
+
21
+ EOL = "\r\n"
22
+ MULTIPART = %r|\Amultipart/.*boundary=\"?([^\";,]+)\"?|ni
23
+ TOKEN = /[^\s()<>,;:\\"\/\[\]?=]+/
24
+ CONDISP = /Content-Disposition:\s*#{TOKEN}\s*/i
25
+ VALUE = /"(?:\\"|[^"])*"|#{TOKEN}/
26
+ BROKEN = /^#{CONDISP}.*;\s*filename=(#{VALUE})/i
27
+ MULTIPART_CONTENT_TYPE = /Content-Type: (.*)#{EOL}/ni
28
+ MULTIPART_CONTENT_DISPOSITION = /Content-Disposition:[^:]*;\s*name=(#{VALUE})/ni
29
+ MULTIPART_CONTENT_ID = /Content-ID:\s*([^#{EOL}]*)/ni
30
+ # Updated definitions from RFC 2231
31
+ ATTRIBUTE_CHAR = %r{[^ \x00-\x1f\x7f)(><@,;:\\"/\[\]?='*%]}
32
+ ATTRIBUTE = /#{ATTRIBUTE_CHAR}+/
33
+ SECTION = /\*[0-9]+/
34
+ REGULAR_PARAMETER_NAME = /#{ATTRIBUTE}#{SECTION}?/
35
+ REGULAR_PARAMETER = /(#{REGULAR_PARAMETER_NAME})=(#{VALUE})/
36
+ EXTENDED_OTHER_NAME = /#{ATTRIBUTE}\*[1-9][0-9]*\*/
37
+ EXTENDED_OTHER_VALUE = /%[0-9a-fA-F]{2}|#{ATTRIBUTE_CHAR}/
38
+ EXTENDED_OTHER_PARAMETER = /(#{EXTENDED_OTHER_NAME})=(#{EXTENDED_OTHER_VALUE}*)/
39
+ EXTENDED_INITIAL_NAME = /#{ATTRIBUTE}(?:\*0)?\*/
40
+ EXTENDED_INITIAL_VALUE = /[a-zA-Z0-9\-]*'[a-zA-Z0-9\-]*'#{EXTENDED_OTHER_VALUE}*/
41
+ EXTENDED_INITIAL_PARAMETER = /(#{EXTENDED_INITIAL_NAME})=(#{EXTENDED_INITIAL_VALUE})/
42
+ EXTENDED_PARAMETER = /#{EXTENDED_INITIAL_PARAMETER}|#{EXTENDED_OTHER_PARAMETER}/
43
+ DISPPARM = /;\s*(?:#{REGULAR_PARAMETER}|#{EXTENDED_PARAMETER})\s*/
44
+ RFC2183 = /^#{CONDISP}(#{DISPPARM})+$/i
11
45
 
46
+ class Parser
12
47
  BUFSIZE = 1_048_576
13
48
  TEXT_PLAIN = "text/plain"
14
49
  TEMPFILE_FACTORY = lambda { |filename, content_type|
15
50
  Tempfile.new(["RackMultipart", ::File.extname(filename.gsub("\0", '%00'))])
16
51
  }
17
52
 
18
- BOUNDARY_REGEX = /\A([^\n]*(?:\n|\Z))/
19
-
20
53
  class BoundedIO # :nodoc:
21
54
  def initialize(io, content_length)
22
55
  @io = io
@@ -38,16 +71,12 @@ module Rack
38
71
  if str
39
72
  @cursor += str.bytesize
40
73
  else
41
- # Raise an error for mismatching Content-Length and actual contents
74
+ # Raise an error for mismatching content-length and actual contents
42
75
  raise EOFError, "bad content body"
43
76
  end
44
77
 
45
78
  str
46
79
  end
47
-
48
- def rewind
49
- @io.rewind
50
- end
51
80
  end
52
81
 
53
82
  MultipartInfo = Struct.new :params, :tmp_files
@@ -66,18 +95,17 @@ module Rack
66
95
  boundary = parse_boundary content_type
67
96
  return EMPTY unless boundary
68
97
 
98
+ if boundary.length > 70
99
+ # RFC 1521 Section 7.2.1 imposes a 70 character maximum for the boundary.
100
+ # Most clients use no more than 55 characters.
101
+ raise Error, "multipart boundary size too large (#{boundary.length} characters)"
102
+ end
103
+
69
104
  io = BoundedIO.new(io, content_length) if content_length
70
- outbuf = String.new
71
105
 
72
106
  parser = new(boundary, tmpfile, bufsize, qp)
73
- parser.on_read io.read(bufsize, outbuf)
107
+ parser.parse(io)
74
108
 
75
- loop do
76
- break if parser.state == :DONE
77
- parser.on_read io.read(bufsize, outbuf)
78
- end
79
-
80
- io.rewind
81
109
  parser.result
82
110
  end
83
111
 
@@ -140,7 +168,7 @@ module Rack
140
168
 
141
169
  @mime_parts[mime_index] = klass.new(body, head, filename, content_type, name)
142
170
 
143
- check_open_files
171
+ check_part_limits
144
172
  end
145
173
 
146
174
  def on_mime_body(mime_index, content)
@@ -152,13 +180,23 @@ module Rack
152
180
 
153
181
  private
154
182
 
155
- def check_open_files
156
- if Utils.multipart_part_limit > 0
157
- if @open_files >= Utils.multipart_part_limit
183
+ def check_part_limits
184
+ file_limit = Utils.multipart_file_limit
185
+ part_limit = Utils.multipart_total_part_limit
186
+
187
+ if file_limit && file_limit > 0
188
+ if @open_files >= file_limit
158
189
  @mime_parts.each(&:close)
159
190
  raise MultipartPartLimitError, 'Maximum file multiparts in content reached'
160
191
  end
161
192
  end
193
+
194
+ if part_limit && part_limit > 0
195
+ if @mime_parts.size >= part_limit
196
+ @mime_parts.each(&:close)
197
+ raise MultipartTotalPartLimitError, 'Maximum total multiparts in content reached'
198
+ end
199
+ end
162
200
  end
163
201
  end
164
202
 
@@ -167,32 +205,46 @@ module Rack
167
205
  def initialize(boundary, tempfile, bufsize, query_parser)
168
206
  @query_parser = query_parser
169
207
  @params = query_parser.make_params
170
- @boundary = "--#{boundary}"
171
208
  @bufsize = bufsize
172
209
 
173
- @full_boundary = @boundary
174
- @end_boundary = @boundary + '--'
175
210
  @state = :FAST_FORWARD
176
211
  @mime_index = 0
177
212
  @collector = Collector.new tempfile
178
213
 
179
214
  @sbuf = StringScanner.new("".dup)
180
- @body_regex = /(?:#{EOL})?#{Regexp.quote(@boundary)}(?:#{EOL}|--)/m
181
- @rx_max_size = EOL.size + @boundary.bytesize + [EOL.size, '--'.size].max
215
+ @body_regex = /(?:#{EOL}|\A)--#{Regexp.quote(boundary)}(?:#{EOL}|--)/m
216
+ @rx_max_size = boundary.bytesize + 6 # (\r\n-- at start, either \r\n or -- at finish)
182
217
  @head_regex = /(.*?#{EOL})#{EOL}/m
183
218
  end
184
219
 
185
- def on_read(content)
186
- handle_empty_content!(content)
187
- @sbuf.concat content
188
- run_parser
220
+ def parse(io)
221
+ outbuf = String.new
222
+ read_data(io, outbuf)
223
+
224
+ loop do
225
+ status =
226
+ case @state
227
+ when :FAST_FORWARD
228
+ handle_fast_forward
229
+ when :CONSUME_TOKEN
230
+ handle_consume_token
231
+ when :MIME_HEAD
232
+ handle_mime_head
233
+ when :MIME_BODY
234
+ handle_mime_body
235
+ else # when :DONE
236
+ return
237
+ end
238
+
239
+ read_data(io, outbuf) if status == :want_read
240
+ end
189
241
  end
190
242
 
191
243
  def result
192
244
  @collector.each do |part|
193
245
  part.get_data do |data|
194
246
  tag_multipart_encoding(part.filename, part.content_type, part.name, data)
195
- @query_parser.normalize_params(@params, part.name, data, @query_parser.param_depth_limit)
247
+ @query_parser.normalize_params(@params, part.name, data)
196
248
  end
197
249
  end
198
250
  MultipartInfo.new @params.to_params_hash, @collector.find_all(&:file?).map(&:body)
@@ -200,29 +252,38 @@ module Rack
200
252
 
201
253
  private
202
254
 
203
- def run_parser
204
- loop do
205
- case @state
206
- when :FAST_FORWARD
207
- break if handle_fast_forward == :want_read
208
- when :CONSUME_TOKEN
209
- break if handle_consume_token == :want_read
210
- when :MIME_HEAD
211
- break if handle_mime_head == :want_read
212
- when :MIME_BODY
213
- break if handle_mime_body == :want_read
214
- when :DONE
215
- break
216
- end
217
- end
255
+ def dequote(str) # From WEBrick::HTTPUtils
256
+ ret = (/\A"(.*)"\Z/ =~ str) ? $1 : str.dup
257
+ ret.gsub!(/\\(.)/, "\\1")
258
+ ret
259
+ end
260
+
261
+ def read_data(io, outbuf)
262
+ content = io.read(@bufsize, outbuf)
263
+ handle_empty_content!(content)
264
+ @sbuf.concat(content)
218
265
  end
219
266
 
267
+ # This handles the initial parser state. We read until we find the starting
268
+ # boundary, then we can transition to the next state. If we find the ending
269
+ # boundary, this is an invalid multipart upload, but keep scanning for opening
270
+ # boundary in that case. If no boundary found, we need to keep reading data
271
+ # and retry. It's highly unlikely the initial read will not consume the
272
+ # boundary. The client would have to deliberately craft a response
273
+ # with the opening boundary beyond the buffer size for that to happen.
220
274
  def handle_fast_forward
221
- if consume_boundary
222
- @state = :MIME_HEAD
223
- else
224
- raise EOFError, "bad content body" if @sbuf.rest_size >= @bufsize
225
- :want_read
275
+ while true
276
+ case consume_boundary
277
+ when :BOUNDARY
278
+ # found opening boundary, transition to next state
279
+ @state = :MIME_HEAD
280
+ return
281
+ when :END_BOUNDARY
282
+ # invalid multipart upload, but retry for opening boundary
283
+ else
284
+ # no boundary found, keep reading data
285
+ return :want_read
286
+ end
226
287
  end
227
288
  end
228
289
 
@@ -241,7 +302,7 @@ module Rack
241
302
  head = @sbuf[1]
242
303
  content_type = head[MULTIPART_CONTENT_TYPE, 1]
243
304
  if name = head[MULTIPART_CONTENT_DISPOSITION, 1]
244
- name = Rack::Auth::Digest::Params::dequote(name)
305
+ name = dequote(name)
245
306
  else
246
307
  name = head[MULTIPART_CONTENT_ID, 1]
247
308
  end
@@ -278,15 +339,16 @@ module Rack
278
339
  end
279
340
  end
280
341
 
281
- def full_boundary; @full_boundary; end
282
-
342
+ # Scan until the we find the start or end of the boundary.
343
+ # If we find it, return the appropriate symbol for the start or
344
+ # end of the boundary. If we don't find the start or end of the
345
+ # boundary, clear the buffer and return nil.
283
346
  def consume_boundary
284
- while read_buffer = @sbuf.scan_until(BOUNDARY_REGEX)
285
- case read_buffer.strip
286
- when full_boundary then return :BOUNDARY
287
- when @end_boundary then return :END_BOUNDARY
288
- end
289
- return if @sbuf.eos?
347
+ if read_buffer = @sbuf.scan_until(@body_regex)
348
+ read_buffer.end_with?(EOL) ? :BOUNDARY : :END_BOUNDARY
349
+ else
350
+ @sbuf.terminate
351
+ nil
290
352
  end
291
353
  end
292
354
 
@@ -296,10 +358,10 @@ module Rack
296
358
  when RFC2183
297
359
  params = Hash[*head.scan(DISPPARM).flat_map(&:compact)]
298
360
 
299
- if filename = params['filename']
300
- filename = $1 if filename =~ /^"(.*)"$/
301
- elsif filename = params['filename*']
361
+ if filename = params['filename*']
302
362
  encoding, _, filename = filename.split("'", 3)
363
+ elsif filename = params['filename']
364
+ filename = $1 if filename =~ /^"(.*)"$/
303
365
  end
304
366
  when BROKEN
305
367
  filename = $1
@@ -326,6 +388,7 @@ module Rack
326
388
  end
327
389
 
328
390
  CHARSET = "charset"
391
+ deprecate_constant :CHARSET
329
392
 
330
393
  def tag_multipart_encoding(filename, content_type, name, body)
331
394
  name = name.to_s
@@ -346,7 +409,13 @@ module Rack
346
409
  k.strip!
347
410
  v.strip!
348
411
  v = v[1..-2] if v.start_with?('"') && v.end_with?('"')
349
- encoding = Encoding.find v if k == CHARSET
412
+ if k == "charset"
413
+ encoding = begin
414
+ Encoding.find v
415
+ rescue ArgumentError
416
+ Encoding::BINARY
417
+ end
418
+ end
350
419
  end
351
420
  end
352
421
  end
@@ -357,7 +426,7 @@ module Rack
357
426
 
358
427
  def handle_empty_content!(content)
359
428
  if content.nil? || content.empty?
360
- raise EOFError
429
+ raise EmptyContentError
361
430
  end
362
431
  end
363
432
  end
@@ -1,8 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'tempfile'
4
+ require 'fileutils'
5
+
3
6
  module Rack
4
7
  module Multipart
5
8
  class UploadedFile
9
+
6
10
  # The filename, *not* including the path, of the "uploaded" file
7
11
  attr_reader :original_filename
8
12
 
@@ -1,64 +1,44 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'constants'
4
+ require_relative 'utils'
5
+
3
6
  require_relative 'multipart/parser'
7
+ require_relative 'multipart/generator'
4
8
 
5
9
  module Rack
6
10
  # A multipart form data parser, adapted from IOWA.
7
11
  #
8
12
  # Usually, Rack::Request#POST takes care of calling this.
9
13
  module Multipart
10
- autoload :UploadedFile, 'rack/multipart/uploaded_file'
11
- autoload :Generator, 'rack/multipart/generator'
12
-
13
- EOL = "\r\n"
14
14
  MULTIPART_BOUNDARY = "AaB03x"
15
- MULTIPART = %r|\Amultipart/.*boundary=\"?([^\";,]+)\"?|ni
16
- TOKEN = /[^\s()<>,;:\\"\/\[\]?=]+/
17
- CONDISP = /Content-Disposition:\s*#{TOKEN}\s*/i
18
- VALUE = /"(?:\\"|[^"])*"|#{TOKEN}/
19
- BROKEN = /^#{CONDISP}.*;\s*filename=(#{VALUE})/i
20
- MULTIPART_CONTENT_TYPE = /Content-Type: (.*)#{EOL}/ni
21
- MULTIPART_CONTENT_DISPOSITION = /Content-Disposition:.*;\s*name=(#{VALUE})/ni
22
- MULTIPART_CONTENT_ID = /Content-ID:\s*([^#{EOL}]*)/ni
23
- # Updated definitions from RFC 2231
24
- ATTRIBUTE_CHAR = %r{[^ \t\v\n\r)(><@,;:\\"/\[\]?='*%]}
25
- ATTRIBUTE = /#{ATTRIBUTE_CHAR}+/
26
- SECTION = /\*[0-9]+/
27
- REGULAR_PARAMETER_NAME = /#{ATTRIBUTE}#{SECTION}?/
28
- REGULAR_PARAMETER = /(#{REGULAR_PARAMETER_NAME})=(#{VALUE})/
29
- EXTENDED_OTHER_NAME = /#{ATTRIBUTE}\*[1-9][0-9]*\*/
30
- EXTENDED_OTHER_VALUE = /%[0-9a-fA-F]{2}|#{ATTRIBUTE_CHAR}/
31
- EXTENDED_OTHER_PARAMETER = /(#{EXTENDED_OTHER_NAME})=(#{EXTENDED_OTHER_VALUE}*)/
32
- EXTENDED_INITIAL_NAME = /#{ATTRIBUTE}(?:\*0)?\*/
33
- EXTENDED_INITIAL_VALUE = /[a-zA-Z0-9\-]*'[a-zA-Z0-9\-]*'#{EXTENDED_OTHER_VALUE}*/
34
- EXTENDED_INITIAL_PARAMETER = /(#{EXTENDED_INITIAL_NAME})=(#{EXTENDED_INITIAL_VALUE})/
35
- EXTENDED_PARAMETER = /#{EXTENDED_INITIAL_PARAMETER}|#{EXTENDED_OTHER_PARAMETER}/
36
- DISPPARM = /;\s*(?:#{REGULAR_PARAMETER}|#{EXTENDED_PARAMETER})\s*/
37
- RFC2183 = /^#{CONDISP}(#{DISPPARM})+$/i
38
15
 
39
16
  class << self
40
17
  def parse_multipart(env, params = Rack::Utils.default_query_parser)
41
- extract_multipart Rack::Request.new(env), params
42
- end
18
+ io = env[RACK_INPUT]
19
+
20
+ if content_length = env['CONTENT_LENGTH']
21
+ content_length = content_length.to_i
22
+ end
43
23
 
44
- def extract_multipart(req, params = Rack::Utils.default_query_parser)
45
- io = req.get_header(RACK_INPUT)
46
- io.rewind
47
- content_length = req.content_length
48
- content_length = content_length.to_i if content_length
24
+ content_type = env['CONTENT_TYPE']
49
25
 
50
- tempfile = req.get_header(RACK_MULTIPART_TEMPFILE_FACTORY) || Parser::TEMPFILE_FACTORY
51
- bufsize = req.get_header(RACK_MULTIPART_BUFFER_SIZE) || Parser::BUFSIZE
26
+ tempfile = env[RACK_MULTIPART_TEMPFILE_FACTORY] || Parser::TEMPFILE_FACTORY
27
+ bufsize = env[RACK_MULTIPART_BUFFER_SIZE] || Parser::BUFSIZE
52
28
 
53
- info = Parser.parse io, content_length, req.get_header('CONTENT_TYPE'), tempfile, bufsize, params
54
- req.set_header(RACK_TEMPFILES, info.tmp_files)
55
- info.params
29
+ info = Parser.parse(io, content_length, content_type, tempfile, bufsize, params)
30
+ env[RACK_TEMPFILES] = info.tmp_files
31
+
32
+ return info.params
33
+ end
34
+
35
+ def extract_multipart(request, params = Rack::Utils.default_query_parser)
36
+ parse_multipart(request.env)
56
37
  end
57
38
 
58
39
  def build_multipart(params, first = true)
59
40
  Generator.new(params, first).dump
60
41
  end
61
42
  end
62
-
63
43
  end
64
44
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'constants'
4
+
3
5
  module Rack
4
6
  class NullLogger
5
7
  def initialize(app)
@@ -22,6 +24,11 @@ module Rack
22
24
  def warn? ; end
23
25
  def error? ; end
24
26
  def fatal? ; end
27
+ def debug! ; end
28
+ def error! ; end
29
+ def fatal! ; end
30
+ def info! ; end
31
+ def warn! ; end
25
32
  def level ; end
26
33
  def progname ; end
27
34
  def datetime_format ; end
@@ -34,6 +41,8 @@ module Rack
34
41
  def sev_threshold=(sev_threshold); end
35
42
  def close ; end
36
43
  def add(severity, message = nil, progname = nil, &block); end
44
+ def log(severity, message = nil, progname = nil, &block); end
37
45
  def <<(msg); end
46
+ def reopen(logdev = nil); end
38
47
  end
39
48
  end