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.