module CGI::QueryExtension

def read_multipart(boundary, content_length)


params[name => body]

Tempfile depending on whether the multipart form element exceeds 10 KB
Returns a hash of multipart form parameters with bodies of type StringIO or

http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2
Parses multipart form elements according to
#
def read_multipart(boundary, content_length)
  ## read first boundary
  stdin = stdinput
  first_line = "--#{boundary}#{EOL}"
  content_length -= first_line.bytesize
  status = stdin.read(first_line.bytesize)
  raise EOFError.new("no content body")  unless status
  raise EOFError.new("bad content body") unless first_line == status
  ## parse and set params
  params = {}
  @files = {}
  boundary_rexp = /--#{Regexp.quote(boundary)}(#{EOL}|--)/
  boundary_size = "#{EOL}--#{boundary}#{EOL}".bytesize
  buf = ''.dup
  bufsize = 10 * 1024
  max_count = MAX_MULTIPART_COUNT
  n = 0
  tempfiles = []
  while true
    (n += 1) < max_count or raise StandardError.new("too many parameters.")
    ## create body (StringIO or Tempfile)
    body = create_body(bufsize < content_length)
    tempfiles << body if defined?(Tempfile) && body.kind_of?(Tempfile)
    class << body
      if method_defined?(:path)
        alias local_path path
      else
        def local_path
          nil
        end
      end
      attr_reader :original_filename, :content_type
    end
    ## find head and boundary
    head = nil
    separator = EOL * 2
    until head && matched = boundary_rexp.match(buf)
      if !head && pos = buf.index(separator)
        len  = pos + EOL.bytesize
        head = buf[0, len]
        buf  = buf[(pos+separator.bytesize)..-1]
      else
        if head && buf.size > boundary_size
          len = buf.size - boundary_size
          body.print(buf[0, len])
          buf[0, len] = ''
        end
        c = stdin.read(bufsize < content_length ? bufsize : content_length)
        raise EOFError.new("bad content body") if c.nil? || c.empty?
        buf << c
        content_length -= c.bytesize
      end
    end
    ## read to end of boundary
    m = matched
    len = m.begin(0)
    s = buf[0, len]
    if s =~ /(\r?\n)\z/
      s = buf[0, len - $1.bytesize]
    end
    body.print(s)
    buf = buf[m.end(0)..-1]
    boundary_end = m[1]
    content_length = -1 if boundary_end == '--'
    ## reset file cursor position
    body.rewind
    ## original filename
    /Content-Disposition:.* filename=(?:"(.*?)"|([^;\r\n]*))/i.match(head)
    filename = $1 || $2 || ''.dup
    filename = CGI.unescape(filename) if unescape_filename?()
    body.instance_variable_set(:@original_filename, filename)
    ## content type
    /Content-Type: (.*)/i.match(head)
    (content_type = $1 || ''.dup).chomp!
    body.instance_variable_set(:@content_type, content_type)
    ## query parameter name
    /Content-Disposition:.* name=(?:"(.*?)"|([^;\r\n]*))/i.match(head)
    name = $1 || $2 || ''
    if body.original_filename.empty?
      value=body.read.dup.force_encoding(@accept_charset)
      body.close! if defined?(Tempfile) && body.kind_of?(Tempfile)
      (params[name] ||= []) << value
      unless value.valid_encoding?
        if @accept_charset_error_block
          @accept_charset_error_block.call(name,value)
        else
          raise InvalidEncoding,"Accept-Charset encoding error"
        end
      end
      class << params[name].last;self;end.class_eval do
        define_method(:read){self}
        define_method(:original_filename){""}
        define_method(:content_type){""}
      end
    else
      (params[name] ||= []) << body
      @files[name]=body
    end
    ## break loop
    break if content_length == -1
  end
  raise EOFError, "bad boundary end of body part" unless boundary_end =~ /--/
  params.default = []
  params
rescue Exception
  if tempfiles
    tempfiles.each {|t|
      if t.path
        t.close!
      end
    }
  end
  raise
end # read_multipart