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