Module: Rack::Utils::Multipart

### Home Classes Methods

### In Files

- lib/rack/utils.rb

### Namespace

- CLASS Rack::Utils::Multipart::UploadedFile

### Methods

- ::build_multipart
- ::parse_multipart

### Files

- KNOWN-ISSUES
- README
- SPEC

### Class/Module Index

Quicksearch

- Rack
- Rack::Auth
- Rack::Auth::AbstractHandler
- Rack::Auth::AbstractRequest
- Rack::Auth::Basic
- Rack::Auth::Basic::Request
- Rack::Auth::Digest
- Rack::Auth::Digest::MD5
- Rack::Auth::Digest::Nonce
- Rack::Auth::Digest::Params
- Rack::Auth::Digest::Request
- Rack::Builder
- Rack::Cascade
- Rack::Chunked
- Rack::CommonLogger
- Rack::ConditionalGet
- Rack::Config
- Rack::ContentLength
- Rack::ContentType
- Rack::Deflater
- Rack::Deflater::DeflateStream
- Rack::Deflater::GzipStream
- Rack::Directory
- Rack::ETag
- Rack::File
- Rack::ForwardRequest
- Rack::Handler
- Rack::Handler::CGI
- Rack::Handler::EventedMongrel
- Rack::Handler::FastCGI
- Rack::Handler::LSWS
- Rack::Handler::Mongrel
- Rack::Handler::SCGI
- Rack::Handler::SwiftipliedMongrel
- Rack::Handler::Thin
- Rack::Handler::WEBrick
- Rack::Head
- Rack::Lint
- Rack::Lobster
- Rack::Lock
- Rack::Logger
- Rack::MethodOverride
- Rack::Mime
- Rack::MockRequest
- Rack::MockRequest::FatalWarner
- Rack::MockRequest::FatalWarning
- Rack::MockResponse
- Rack::NullLogger
- Rack::Recursive
- Rack::Reloader
- Rack::Reloader::Stat
- Rack::Request
- Rack::Response
- Rack::Response::Helpers
- Rack::RewindableInput
- Rack::RewindableInput::Tempfile
- Rack::Runtime
- Rack::Sendfile
- Rack::Server
- Rack::Server::Options
- Rack::Session
- Rack::Session::Abstract
- Rack::Session::Abstract::ID
- Rack::Session::Cookie
- Rack::Session::Memcache
- Rack::Session::Pool
- Rack::ShowExceptions
- Rack::ShowStatus
- Rack::Static
- Rack::URLMap
- Rack::Utils
- Rack::Utils::Context
- Rack::Utils::HeaderHash
- Rack::Utils::Multipart
- Rack::Utils::Multipart::UploadedFile
- FCGI
- FCGI::Stream

No matching classes.

# Rack::Utils::Multipart

A multipart form data parser, adapted from IOWA.

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

### Constants

EOL
MULTIPART_BOUNDARY

### Public Class Methods

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

    # File lib/rack/utils.rb, line 595
          def self.build_multipart(params, first = true)
            if first
              unless params.is_a?(Hash)
                raise ArgumentError, "value must be a Hash"
              end

              multipart = false
              query = lambda { |value|
                case value
                when Array
                  value.each(&query)
                when Hash
                  value.values.each(&query)
                when UploadedFile
                  multipart = true
                end
              }
              params.values.each(&query)
              return nil unless multipart
            end

            flattened_params = Hash.new

            params.each do |key, value|
              k = first ? key.to_s : "[#{key}]"

              case value
              when Array
                value.map { |v|
                  build_multipart(v, false).each { |subkey, subvalue|
                    flattened_params["#{k}[]#{subkey}"] = subvalue
                  }
                }
              when Hash
                build_multipart(value, false).each { |subkey, subvalue|
                  flattened_params[k + subkey] = subvalue
                }
              else
                flattened_params[k] = value
              end
            end

            if first
              flattened_params.map { |name, file|
                if file.respond_to?(:original_filename)
                  ::File.open(file.path, "rb") do |f|
                    f.set_encoding(Encoding::BINARY) if f.respond_to?(:set_encoding)
    --#{MULTIPART_BOUNDARY}\rContent-Disposition: form-data; name="#{name}"; filename="#{Utils.escape(file.original_filename)}"\rContent-Type: #{file.content_type}\rContent-Length: #{::File.stat(file.path).size}\r\r#{f.read}\r
                  end
                else
    --#{MULTIPART_BOUNDARY}\rContent-Disposition: form-data; name="#{name}"\r\r#{file}\r
                end
              }.join + "--#{MULTIPART_BOUNDARY}--\r"
            else
              flattened_params
            end
          end

parse_multipart(env) click to toggle source

    # File lib/rack/utils.rb, line 473
    def self.parse_multipart(env)
      unless env['CONTENT_TYPE'] =~
          %\Amultipart/.*boundary=\"?([^\";,]+)\"?|
        nil
      else
        boundary = "--#{$1}"

        params = {}
        buf = ""
        content_length = env['CONTENT_LENGTH'].to_i
        input = env['rack.input']
        input.rewind

        boundary_size = Utils.bytesize(boundary) + EOL.size
        bufsize = 16384

        content_length -= boundary_size

        read_buffer = ''

        status = input.read(boundary_size, read_buffer)
        raise EOFError, "bad content body"  unless status == boundary + EOL

        rx = /(?:#{EOL})?#{Regexp.quote boundary}(#{EOL}|--)/

        loop {
          head = nil
          body = ''
          filename = content_type = name = nil

          until head && buf =~ rx
            if !head && i = buf.index(EOL+EOL)
              head = buf.slice!(0, i+2) # First \r\n
              buf.slice!(0, 2)          # Second \r\n

              token = /[^\s()<>,;:\\"\/\[\]?=]+/
              condisp = /Content-Disposition:\s*#{token}\s*/
              dispparm = /;\s*(#{token})=("(?:\\"|[^"])*"|#{token})*/

              rfc2183 = /^#{condisp}(#{dispparm})+$/
              broken_quoted = /^#{condisp}.*;\sfilename="(.*?)"(?:\s*$|\s*;\s*#{token}=)/
              broken_unquoted = /^#{condisp}.*;\sfilename=(#{token})/

              if head =~ rfc2183
                filename = Hash[head.scan(dispparm)]['filename']
                filename = $1 if filename and filename =~ /^"(.*)"$/
              elsif head =~ broken_quoted
                filename = $1
              elsif head =~ broken_unquoted
                filename = $1
              end

              if filename && filename !~ /\\[^\\"]/
                filename = Utils.unescape(filename).gsub(/\\(.)/, '\1')
              end

              content_type = head[/Content-Type: (.*)#{EOL}/i, 1]
              name = head[/Content-Disposition:.*\s+name="?([^\";]*)"?/i, 1] || head[/Content-ID:\s*([^#{EOL}]*)/i, 1]

              if filename
                body = Tempfile.new("RackMultipart")
                body.binmode  if body.respond_to?(:binmode)
              end

              next
            end

            # Save the read body part.
            if head && (boundary_size+4 < buf.size)
              body << buf.slice!(0, buf.size - (boundary_size+4))
            end

            c = input.read(bufsize < content_length ? bufsize : content_length, read_buffer)
            raise EOFError, "bad content body"  if c.nil? || c.empty?
            buf << c
            content_length -= c.size
          end

          # Save the rest.
          if i = buf.index(rx)
            body << buf.slice!(0, i)
            buf.slice!(0, boundary_size+2)

            content_length = -1  if $1 == "--"
          end

          if filename == ""
            # filename is blank which means no file has been selected
            data = nil
          elsif filename
            body.rewind

            # Take the basename of the upload's original filename.
            # This handles the full Windows paths given by Internet Explorer
            # (and perhaps other broken user agents) without affecting
            # those which give the lone filename.
            filename = filename.split(/[\/\\]/).last

            data = {:filename => filename, :type => content_type,
                    :name => name, :tempfile => body, :head => head}
          elsif !filename && content_type
            body.rewind

            # Generic multipart cases, not coming from a form
            data = {:type => content_type,
                    :name => name, :tempfile => body, :head => head}
          else
            data = body
          end

          Utils.normalize_params(params, name, data) unless data.nil?

          # break if we're at the end of a buffer, but not if it is the end of a field
          break if (buf.empty? && $1 != EOL) || content_length == -1
        }

        input.rewind

        params
      end
    end

[Validate]

Generated with the Darkfish Rdoc Generator 2.