| 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 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