A multipart form data parser, adapted from IOWA.
Usually, Rack::Request#POST takes care of calling this.
(Not documented)
# File lib/rack/utils.rb, line 294
294: def self.parse_multipart(env)
295: unless env['CONTENT_TYPE'] =~
296: %r|\Amultipart/.*boundary=\"?([^\";,]+)\"?|n
297: nil
298: else
299: boundary = "--#{$1}"
300:
301: params = {}
302: buf = ""
303: content_length = env['CONTENT_LENGTH'].to_i
304: input = env['rack.input']
305: input.rewind
306:
307: boundary_size = boundary.size + EOL.size
308: bufsize = 16384
309:
310: content_length -= boundary_size
311:
312: read_buffer = ''
313:
314: status = input.read(boundary_size, read_buffer)
315: raise EOFError, "bad content body" unless status == boundary + EOL
316:
317: rx = /(?:#{EOL})?#{Regexp.quote boundary}(#{EOL}|--)/n
318:
319: loop {
320: head = nil
321: body = ''
322: filename = content_type = name = nil
323:
324: until head && buf =~ rx
325: if !head && i = buf.index(EOL+EOL)
326: head = buf.slice!(0, i+2) # First \r\n
327: buf.slice!(0, 2) # Second \r\n
328:
329: filename = head[/Content-Disposition:.* filename="?([^\";]*)"?/ni, 1]
330: content_type = head[/Content-Type: (.*)#{EOL}/ni, 1]
331: name = head[/Content-Disposition:.*\s+name="?([^\";]*)"?/ni, 1] || head[/Content-ID:\s*([^#{EOL}]*)/ni, 1]
332:
333: if content_type || filename
334: body = Tempfile.new("RackMultipart")
335: body.binmode if body.respond_to?(:binmode)
336: end
337:
338: next
339: end
340:
341: # Save the read body part.
342: if head && (boundary_size+4 < buf.size)
343: body << buf.slice!(0, buf.size - (boundary_size+4))
344: end
345:
346: c = input.read(bufsize < content_length ? bufsize : content_length, read_buffer)
347: raise EOFError, "bad content body" if c.nil? || c.empty?
348: buf << c
349: content_length -= c.size
350: end
351:
352: # Save the rest.
353: if i = buf.index(rx)
354: body << buf.slice!(0, i)
355: buf.slice!(0, boundary_size+2)
356:
357: content_length = -1 if $1 == "--"
358: end
359:
360: if filename == ""
361: # filename is blank which means no file has been selected
362: data = nil
363: elsif filename
364: body.rewind
365:
366: # Take the basename of the upload's original filename.
367: # This handles the full Windows paths given by Internet Explorer
368: # (and perhaps other broken user agents) without affecting
369: # those which give the lone filename.
370: filename =~ /^(?:.*[:\\\/])?(.*)/m
371: filename = $1
372:
373: data = {:filename => filename, :type => content_type,
374: :name => name, :tempfile => body, :head => head}
375: elsif !filename && content_type
376: body.rewind
377:
378: # Generic multipart cases, not coming from a form
379: data = {:type => content_type,
380: :name => name, :tempfile => body, :head => head}
381: else
382: data = body
383: end
384:
385: Utils.normalize_params(params, name, data) unless data.nil?
386:
387: break if buf.empty? || content_length == -1
388: }
389:
390: input.rewind
391:
392: params
393: end
394: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.