| 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 91
91: def self.parse_multipart(env)
92: unless env['CONTENT_TYPE'] =~
93: %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n
94: nil
95: else
96: boundary = "--#{$1}"
97:
98: params = {}
99: buf = ""
100: content_length = env['CONTENT_LENGTH'].to_i
101: input = env['rack.input']
102:
103: boundary_size = boundary.size + EOL.size
104: bufsize = 16384
105:
106: content_length -= boundary_size
107:
108: status = input.read(boundary_size)
109: raise EOFError, "bad content body" unless status == boundary + EOL
110:
111: rx = /(?:#{EOL})?#{Regexp.quote boundary}(#{EOL}|--)/
112:
113: loop {
114: head = nil
115: body = ''
116: filename = content_type = name = nil
117:
118: until head && buf =~ rx
119: if !head && i = buf.index("\r\n\r\n")
120: head = buf.slice!(0, i+2) # First \r\n
121: buf.slice!(0, 2) # Second \r\n
122:
123: filename = head[/Content-Disposition:.* filename="?([^\";]*)"?/ni, 1]
124: content_type = head[/Content-Type: (.*)\r\n/ni, 1]
125: name = head[/Content-Disposition:.* name="?([^\";]*)"?/ni, 1]
126:
127: body = Tempfile.new("RackMultipart") if filename
128:
129: next
130: end
131:
132: # Save the read body part.
133: if head && (boundary_size+4 < buf.size)
134: body << buf.slice!(0, buf.size - (boundary_size+4))
135: end
136:
137: c = input.read(bufsize < content_length ? bufsize : content_length)
138: raise EOFError, "bad content body" if c.nil? || c.empty?
139: buf << c
140: content_length -= c.size
141: end
142:
143: # Save the rest.
144: if i = buf.index(rx)
145: body << buf.slice!(0, i)
146: buf.slice!(0, boundary_size+2)
147:
148: content_length = -1 if $1 == "--"
149: end
150:
151: if filename
152: body.rewind
153: data = {:filename => filename, :type => content_type,
154: :name => name, :tempfile => body, :head => head}
155: else
156: data = body
157: end
158:
159: if name
160: if name =~ /\[\]\z/
161: params[name] ||= []
162: params[name] << data
163: else
164: params[name] = data
165: end
166: end
167:
168: break if buf.empty? || content_length == -1
169: }
170:
171: params
172: end
173: end