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