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 259
259:       def self.parse_multipart(env)
260:         unless env['CONTENT_TYPE'] =~
261:             %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n
262:           nil
263:         else
264:           boundary = "--#{$1}"
265: 
266:           params = {}
267:           buf = ""
268:           content_length = env['CONTENT_LENGTH'].to_i
269:           input = env['rack.input']
270: 
271:           boundary_size = boundary.size + EOL.size
272:           bufsize = 16384
273: 
274:           content_length -= boundary_size
275: 
276:           status = input.read(boundary_size)
277:           raise EOFError, "bad content body"  unless status == boundary + EOL
278: 
279:           rx = /(?:#{EOL})?#{Regexp.quote boundary}(#{EOL}|--)/
280: 
281:           loop {
282:             head = nil
283:             body = ''
284:             filename = content_type = name = nil
285: 
286:             until head && buf =~ rx
287:               if !head && i = buf.index("\r\n\r\n")
288:                 head = buf.slice!(0, i+2) # First \r\n
289:                 buf.slice!(0, 2)          # Second \r\n
290: 
291:                 filename = head[/Content-Disposition:.* filename="?([^\";]*)"?/ni, 1]
292:                 content_type = head[/Content-Type: (.*)\r\n/ni, 1]
293:                 name = head[/Content-Disposition:.* name="?([^\";]*)"?/ni, 1]
294: 
295:                 if filename
296:                   body = Tempfile.new("RackMultipart")
297:                   body.binmode  if body.respond_to?(:binmode)
298:                 end
299: 
300:                 next
301:               end
302: 
303:               # Save the read body part.
304:               if head && (boundary_size+4 < buf.size)
305:                 body << buf.slice!(0, buf.size - (boundary_size+4))
306:               end
307: 
308:               c = input.read(bufsize < content_length ? bufsize : content_length)
309:               raise EOFError, "bad content body"  if c.nil? || c.empty?
310:               buf << c
311:               content_length -= c.size
312:             end
313: 
314:             # Save the rest.
315:             if i = buf.index(rx)
316:               body << buf.slice!(0, i)
317:               buf.slice!(0, boundary_size+2)
318: 
319:               content_length = -1  if $1 == "--"
320:             end
321: 
322:             if filename
323:               body.rewind
324:               data = {:filename => filename, :type => content_type,
325:                       :name => name, :tempfile => body, :head => head}
326:             else
327:               data = body
328:             end
329: 
330:             if name
331:               if name =~ /\[\]\z/
332:                 params[name] ||= []
333:                 params[name] << data
334:               else
335:                 params[name] = data
336:               end
337:             end
338: 
339:             break  if buf.empty? || content_length == -1
340:           }
341: 
342:           params
343:         end
344:       end

[Validate]