| 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.
| EOL | = | "\r\n" |
# File lib/rack/utils.rb, line 293
293: def self.parse_multipart(env)
294: unless env['CONTENT_TYPE'] =~
295: %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n
296: nil
297: else
298: boundary = "--#{$1}"
299:
300: params = {}
301: buf = ""
302: content_length = env['CONTENT_LENGTH'].to_i
303: input = env['rack.input']
304:
305: boundary_size = boundary.size + EOL.size
306: bufsize = 16384
307:
308: content_length -= boundary_size
309:
310: status = input.read(boundary_size)
311: raise EOFError, "bad content body" unless status == boundary + EOL
312:
313: rx = /(?:#{EOL})?#{Regexp.quote boundary}(#{EOL}|--)/n
314:
315: loop {
316: head = nil
317: body = ''
318: filename = content_type = name = nil
319:
320: until head && buf =~ rx
321: if !head && i = buf.index("\r\n\r\n")
322: head = buf.slice!(0, i+2) # First \r\n
323: buf.slice!(0, 2) # Second \r\n
324:
325: filename = head[/Content-Disposition:.* filename="?([^\";]*)"?/ni, 1]
326: content_type = head[/Content-Type: (.*)\r\n/ni, 1]
327: name = head[/Content-Disposition:.* name="?([^\";]*)"?/ni, 1]
328:
329: if filename
330: body = Tempfile.new("RackMultipart")
331: body.binmode if body.respond_to?(:binmode)
332: end
333:
334: next
335: end
336:
337: # Save the read body part.
338: if head && (boundary_size+4 < buf.size)
339: body << buf.slice!(0, buf.size - (boundary_size+4))
340: end
341:
342: c = input.read(bufsize < content_length ? bufsize : content_length)
343: raise EOFError, "bad content body" if c.nil? || c.empty?
344: buf << c
345: content_length -= c.size
346: end
347:
348: # Save the rest.
349: if i = buf.index(rx)
350: body << buf.slice!(0, i)
351: buf.slice!(0, boundary_size+2)
352:
353: content_length = -1 if $1 == "--"
354: end
355:
356: if filename == ""
357: # filename is blank which means no file has been selected
358: data = nil
359: elsif filename
360: body.rewind
361:
362: # Take the basename of the upload's original filename.
363: # This handles the full Windows paths given by Internet Explorer
364: # (and perhaps other broken user agents) without affecting
365: # those which give the lone filename.
366: filename =~ /^(?:.*[:\\\/])?(.*)/m
367: filename = $1
368:
369: data = {:filename => filename, :type => content_type,
370: :name => name, :tempfile => body, :head => head}
371: else
372: data = body
373: end
374:
375: Utils.normalize_params(params, name, data) unless data.nil?
376:
377: break if buf.empty? || content_length == -1
378: }
379:
380: begin
381: input.rewind if input.respond_to?(:rewind)
382: rescue Errno::ESPIPE
383: # Handles exceptions raised by input streams that cannot be rewound
384: # such as when using plain CGI under Apache
385: end
386:
387: params
388: end
389: end