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 230
230:       def self.parse_multipart(env)
231:         unless env['CONTENT_TYPE'] =~
232:             %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n
233:           nil
234:         else
235:           boundary = "--#{$1}"
236: 
237:           params = {}
238:           buf = ""
239:           content_length = env['CONTENT_LENGTH'].to_i
240:           input = env['rack.input']
241: 
242:           boundary_size = boundary.size + EOL.size
243:           bufsize = 16384
244: 
245:           content_length -= boundary_size
246: 
247:           status = input.read(boundary_size)
248:           raise EOFError, "bad content body"  unless status == boundary + EOL
249: 
250:           rx = /(?:#{EOL})?#{Regexp.quote boundary}(#{EOL}|--)/
251: 
252:           loop {
253:             head = nil
254:             body = ''
255:             filename = content_type = name = nil
256: 
257:             until head && buf =~ rx
258:               if !head && i = buf.index("\r\n\r\n")
259:                 head = buf.slice!(0, i+2) # First \r\n
260:                 buf.slice!(0, 2)          # Second \r\n
261: 
262:                 filename = head[/Content-Disposition:.* filename="?([^\";]*)"?/ni, 1]
263:                 content_type = head[/Content-Type: (.*)\r\n/ni, 1]
264:                 name = head[/Content-Disposition:.* name="?([^\";]*)"?/ni, 1]
265: 
266:                 if filename
267:                   body = Tempfile.new("RackMultipart")
268:                   body.binmode  if body.respond_to?(:binmode)
269:                 end
270: 
271:                 next
272:               end
273: 
274:               # Save the read body part.
275:               if head && (boundary_size+4 < buf.size)
276:                 body << buf.slice!(0, buf.size - (boundary_size+4))
277:               end
278: 
279:               c = input.read(bufsize < content_length ? bufsize : content_length)
280:               raise EOFError, "bad content body"  if c.nil? || c.empty?
281:               buf << c
282:               content_length -= c.size
283:             end
284: 
285:             # Save the rest.
286:             if i = buf.index(rx)
287:               body << buf.slice!(0, i)
288:               buf.slice!(0, boundary_size+2)
289: 
290:               content_length = -1  if $1 == "--"
291:             end
292: 
293:             if filename
294:               body.rewind
295:               data = {:filename => filename, :type => content_type,
296:                       :name => name, :tempfile => body, :head => head}
297:             else
298:               data = body
299:             end
300: 
301:             if name
302:               if name =~ /\[\]\z/
303:                 params[name] ||= []
304:                 params[name] << data
305:               else
306:                 params[name] = data
307:               end
308:             end
309: 
310:             break  if buf.empty? || content_length == -1
311:           }
312: 
313:           params
314:         end
315:       end

[Validate]