| 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 154
154: def self.parse_multipart(env)
155: unless env['CONTENT_TYPE'] =~
156: %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n
157: nil
158: else
159: boundary = "--#{$1}"
160:
161: params = {}
162: buf = ""
163: content_length = env['CONTENT_LENGTH'].to_i
164: input = env['rack.input']
165:
166: boundary_size = boundary.size + EOL.size
167: bufsize = 16384
168:
169: content_length -= boundary_size
170:
171: status = input.read(boundary_size)
172: raise EOFError, "bad content body" unless status == boundary + EOL
173:
174: rx = /(?:#{EOL})?#{Regexp.quote boundary}(#{EOL}|--)/
175:
176: loop {
177: head = nil
178: body = ''
179: filename = content_type = name = nil
180:
181: until head && buf =~ rx
182: if !head && i = buf.index("\r\n\r\n")
183: head = buf.slice!(0, i+2) # First \r\n
184: buf.slice!(0, 2) # Second \r\n
185:
186: filename = head[/Content-Disposition:.* filename="?([^\";]*)"?/ni, 1]
187: content_type = head[/Content-Type: (.*)\r\n/ni, 1]
188: name = head[/Content-Disposition:.* name="?([^\";]*)"?/ni, 1]
189:
190: body = Tempfile.new("RackMultipart") if filename
191:
192: next
193: end
194:
195: # Save the read body part.
196: if head && (boundary_size+4 < buf.size)
197: body << buf.slice!(0, buf.size - (boundary_size+4))
198: end
199:
200: c = input.read(bufsize < content_length ? bufsize : content_length)
201: raise EOFError, "bad content body" if c.nil? || c.empty?
202: buf << c
203: content_length -= c.size
204: end
205:
206: # Save the rest.
207: if i = buf.index(rx)
208: body << buf.slice!(0, i)
209: buf.slice!(0, boundary_size+2)
210:
211: content_length = -1 if $1 == "--"
212: end
213:
214: if filename
215: body.rewind
216: data = {:filename => filename, :type => content_type,
217: :name => name, :tempfile => body, :head => head}
218: else
219: data = body
220: end
221:
222: if name
223: if name =~ /\[\]\z/
224: params[name] ||= []
225: params[name] << data
226: else
227: params[name] = data
228: end
229: end
230:
231: break if buf.empty? || content_length == -1
232: }
233:
234: params
235: end
236: end