Module Rack::Utils::Multipart
In: lib/rack/utils.rb

A multipart form data parser, adapted from IOWA.

Usually, Rack::Request#POST takes care of calling this.

Methods

Constants

EOL = "\r\n"

Public Class methods

[Source]

     # File lib/rack/utils.rb, line 293
293:       def self.parse_multipart(env)
294:         unless env['CONTENT_TYPE'] =~
295:             %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n
296:           nil
297:         else
298:           boundary = "--#{$1}"
299: 
300:           params = {}
301:           buf = ""
302:           content_length = env['CONTENT_LENGTH'].to_i
303:           input = env['rack.input']
304: 
305:           boundary_size = boundary.size + EOL.size
306:           bufsize = 16384
307: 
308:           content_length -= boundary_size
309: 
310:           status = input.read(boundary_size)
311:           raise EOFError, "bad content body"  unless status == boundary + EOL
312: 
313:           rx = /(?:#{EOL})?#{Regexp.quote boundary}(#{EOL}|--)/n
314: 
315:           loop {
316:             head = nil
317:             body = ''
318:             filename = content_type = name = nil
319: 
320:             until head && buf =~ rx
321:               if !head && i = buf.index("\r\n\r\n")
322:                 head = buf.slice!(0, i+2) # First \r\n
323:                 buf.slice!(0, 2)          # Second \r\n
324: 
325:                 filename = head[/Content-Disposition:.* filename="?([^\";]*)"?/ni, 1]
326:                 content_type = head[/Content-Type: (.*)\r\n/ni, 1]
327:                 name = head[/Content-Disposition:.* name="?([^\";]*)"?/ni, 1]
328: 
329:                 if filename
330:                   body = Tempfile.new("RackMultipart")
331:                   body.binmode  if body.respond_to?(:binmode)
332:                 end
333: 
334:                 next
335:               end
336: 
337:               # Save the read body part.
338:               if head && (boundary_size+4 < buf.size)
339:                 body << buf.slice!(0, buf.size - (boundary_size+4))
340:               end
341: 
342:               c = input.read(bufsize < content_length ? bufsize : content_length)
343:               raise EOFError, "bad content body"  if c.nil? || c.empty?
344:               buf << c
345:               content_length -= c.size
346:             end
347: 
348:             # Save the rest.
349:             if i = buf.index(rx)
350:               body << buf.slice!(0, i)
351:               buf.slice!(0, boundary_size+2)
352: 
353:               content_length = -1  if $1 == "--"
354:             end
355: 
356:             if filename == ""
357:               # filename is blank which means no file has been selected
358:               data = nil
359:             elsif filename
360:               body.rewind
361: 
362:               # Take the basename of the upload's original filename.
363:               # This handles the full Windows paths given by Internet Explorer
364:               # (and perhaps other broken user agents) without affecting
365:               # those which give the lone filename.
366:               filename =~ /^(?:.*[:\\\/])?(.*)/m
367:               filename = $1
368: 
369:               data = {:filename => filename, :type => content_type,
370:                       :name => name, :tempfile => body, :head => head}
371:             else
372:               data = body
373:             end
374: 
375:             Utils.normalize_params(params, name, data) unless data.nil?
376: 
377:             break  if buf.empty? || content_length == -1
378:           }
379: 
380:           begin
381:             input.rewind if input.respond_to?(:rewind)
382:           rescue Errno::ESPIPE
383:             # Handles exceptions raised by input streams that cannot be rewound
384:             # such as when using plain CGI under Apache
385:           end
386: 
387:           params
388:         end
389:       end

[Validate]