Namespace

Class Index [+]

Quicksearch

Multipart

A multipart form data parser, adapted from IOWA.

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

Constants

EOL
(Not documented)
MULTIPART_BOUNDARY
(Not documented)

Public Class Methods

build_multipart(params, first = true) click to toggle source

(Not documented)

     # File lib/rack/utils.rb, line 549
549:       def self.build_multipart(params, first = true)
550:         if first
551:           unless params.is_a?(Hash)
552:             raise ArgumentError, "value must be a Hash"
553:           end
554: 
555:           multipart = false
556:           query = lambda { |value|
557:             case value
558:             when Array
559:               value.each(&query)
560:             when Hash
561:               value.values.each(&query)
562:             when UploadedFile
563:               multipart = true
564:             end
565:           }
566:           params.values.each(&query)
567:           return nil unless multipart
568:         end
569: 
570:         flattened_params = Hash.new
571: 
572:         params.each do |key, value|
573:           k = first ? key.to_s : "[#{key}]"
574: 
575:           case value
576:           when Array
577:             value.map { |v|
578:               build_multipart(v, false).each { |subkey, subvalue|
579:                 flattened_params["#{k}[]#{subkey}"] = subvalue
580:               }
581:             }
582:           when Hash
583:             build_multipart(value, false).each { |subkey, subvalue|
584:               flattened_params[k + subkey] = subvalue
585:             }
586:           else
587:             flattened_params[k] = value
588:           end
589:         end
590: 
591:         if first
592:           flattened_params.map { |name, file|
593:             if file.respond_to?(:original_filename)
594:               ::File.open(file.path, "rb") do |f|
595:                 f.set_encoding(Encoding::BINARY) if f.respond_to?(:set_encoding)
596: "--\#{MULTIPART_BOUNDARY}\\r\nContent-Disposition: form-data; name=\"\#{name}\"; filename=\"\#{Utils.escape(file.original_filename)}\"\\r\nContent-Type: \#{file.content_type}\\r\nContent-Length: \#{::File.stat(file.path).size}\\r\n\\r\n\#{f.read}\\r\n"
597:               end
598:             else
599: "--\#{MULTIPART_BOUNDARY}\\r\nContent-Disposition: form-data; name=\"\#{name}\"\\r\n\\r\n\#{file}\\r\n"
600:             end
601:           }.join + "--#{MULTIPART_BOUNDARY}--\r"
602:         else
603:           flattened_params
604:         end
605:       end
parse_multipart(env) click to toggle source

(Not documented)

     # File lib/rack/utils.rb, line 446
446:       def self.parse_multipart(env)
447:         unless env['CONTENT_TYPE'] =~
448:             %r|\Amultipart/.*boundary=\"?([^\";,]+)\"?|n
449:           nil
450:         else
451:           boundary = "--#{$1}"
452: 
453:           params = {}
454:           buf = ""
455:           content_length = env['CONTENT_LENGTH'].to_i
456:           input = env['rack.input']
457:           input.rewind
458: 
459:           boundary_size = Utils.bytesize(boundary) + EOL.size
460:           bufsize = 16384
461: 
462:           content_length -= boundary_size
463: 
464:           read_buffer = ''
465: 
466:           status = input.read(boundary_size, read_buffer)
467:           raise EOFError, "bad content body"  unless status == boundary + EOL
468: 
469:           rx = /(?:#{EOL})?#{Regexp.quote boundary}(#{EOL}|--)/n
470: 
471:           loop {
472:             head = nil
473:             body = ''
474:             filename = content_type = name = nil
475: 
476:             until head && buf =~ rx
477:               if !head && i = buf.index(EOL+EOL)
478:                 head = buf.slice!(0, i+2) # First \r\n
479:                 buf.slice!(0, 2)          # Second \r\n
480: 
481:                 filename = head[/Content-Disposition:.* filename=(?:"((?:\\.|[^\"])*)"|([^;\s]*))/ni, 1]
482:                 content_type = head[/Content-Type: (.*)#{EOL}/ni, 1]
483:                 name = head[/Content-Disposition:.*\s+name="?([^\";]*)"?/ni, 1] || head[/Content-ID:\s*([^#{EOL}]*)/ni, 1]
484: 
485:                 if content_type || filename
486:                   body = Tempfile.new("RackMultipart")
487:                   body.binmode  if body.respond_to?(:binmode)
488:                 end
489: 
490:                 next
491:               end
492: 
493:               # Save the read body part.
494:               if head && (boundary_size+4 < buf.size)
495:                 body << buf.slice!(0, buf.size - (boundary_size+4))
496:               end
497: 
498:               c = input.read(bufsize < content_length ? bufsize : content_length, read_buffer)
499:               raise EOFError, "bad content body"  if c.nil? || c.empty?
500:               buf << c
501:               content_length -= c.size
502:             end
503: 
504:             # Save the rest.
505:             if i = buf.index(rx)
506:               body << buf.slice!(0, i)
507:               buf.slice!(0, boundary_size+2)
508: 
509:               content_length = -1  if $1 == "--"
510:             end
511: 
512:             if filename == ""
513:               # filename is blank which means no file has been selected
514:               data = nil
515:             elsif filename
516:               body.rewind
517: 
518:               # Take the basename of the upload's original filename.
519:               # This handles the full Windows paths given by Internet Explorer
520:               # (and perhaps other broken user agents) without affecting
521:               # those which give the lone filename.
522:               filename =~ /^(?:.*[:\\\/])?(.*)/m
523:               filename = $1
524: 
525:               data = {:filename => filename, :type => content_type,
526:                       :name => name, :tempfile => body, :head => head}
527:             elsif !filename && content_type
528:               body.rewind
529: 
530:               # Generic multipart cases, not coming from a form
531:               data = {:type => content_type,
532:                       :name => name, :tempfile => body, :head => head}
533:             else
534:               data = body
535:             end
536: 
537:             Utils.normalize_params(params, name, data) unless data.nil?
538: 
539:             # break if we're at the end of a buffer, but not if it is the end of a field
540:             break if (buf.empty? && $1 != EOL) || content_length == -1
541:           }
542: 
543:           input.rewind
544: 
545:           params
546:         end
547:       end

Disabled; run with --debug to generate this.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.