Class: Rack::Multipart::Parser

Inherits:
Object
  • Object
show all
Defined in:
lib/rack/multipart/parser.rb

Defined Under Namespace

Classes: BoundedIO, Collector, MultipartInfo

Constant Summary collapse

BUFSIZE =
1_048_576
TEXT_PLAIN =
"text/plain"
TEMPFILE_FACTORY =
lambda { |filename, content_type|
  extension = ::File.extname(filename.gsub("\0", '%00'))[0, 129]

  Tempfile.new(["RackMultipart", extension])
}
EMPTY =
MultipartInfo.new(nil, [])

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(boundary, tempfile, bufsize, query_parser) ⇒ Parser

Returns a new instance of Parser.



200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/rack/multipart/parser.rb', line 200

def initialize(boundary, tempfile, bufsize, query_parser)
  @query_parser   = query_parser
  @params         = query_parser.make_params
  @bufsize        = bufsize

  @state = :FAST_FORWARD
  @mime_index = 0
  @collector = Collector.new tempfile

  @sbuf = StringScanner.new("".dup)
  @body_regex = /(?:#{EOL}|\A)--#{Regexp.quote(boundary)}(?:#{EOL}|--)/m
  @body_regex_at_end = /#{@body_regex}\z/m
  @end_boundary_size = boundary.bytesize + 4 # (-- at start, -- at finish)
  @rx_max_size = boundary.bytesize + 6 # (\r\n-- at start, either \r\n or -- at finish)
  @head_regex = /(.*?#{EOL})#{EOL}/m
end

Instance Attribute Details

#stateObject (readonly)

Returns the value of attribute state.



198
199
200
# File 'lib/rack/multipart/parser.rb', line 198

def state
  @state
end

Class Method Details

.parse(io, content_length, content_type, tmpfile, bufsize, qp) ⇒ Object



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/rack/multipart/parser.rb', line 87

def self.parse(io, content_length, content_type, tmpfile, bufsize, qp)
  return EMPTY if 0 == content_length

  boundary = parse_boundary content_type
  return EMPTY unless boundary

  if boundary.length > 70
    # RFC 1521 Section 7.2.1 imposes a 70 character maximum for the boundary.
    # Most clients use no more than 55 characters.
    raise BoundaryTooLongError, "multipart boundary size too large (#{boundary.length} characters)"
  end

  io = BoundedIO.new(io, content_length) if content_length

  parser = new(boundary, tmpfile, bufsize, qp)
  parser.parse(io)

  parser.result
end

.parse_boundary(content_type) ⇒ Object



80
81
82
83
84
85
# File 'lib/rack/multipart/parser.rb', line 80

def self.parse_boundary(content_type)
  return unless content_type
  data = content_type.match(MULTIPART)
  return unless data
  data[1]
end

Instance Method Details

#parse(io) ⇒ Object



217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/rack/multipart/parser.rb', line 217

def parse(io)
  outbuf = String.new
  read_data(io, outbuf)

  loop do
    status =
      case @state
      when :FAST_FORWARD
        handle_fast_forward
      when :CONSUME_TOKEN
        handle_consume_token
      when :MIME_HEAD
        handle_mime_head
      when :MIME_BODY
        handle_mime_body
      else # when :DONE
        return
      end

    read_data(io, outbuf) if status == :want_read
  end
end

#resultObject



240
241
242
243
244
245
246
247
248
# File 'lib/rack/multipart/parser.rb', line 240

def result
  @collector.each do |part|
    part.get_data do |data|
      tag_multipart_encoding(part.filename, part.content_type, part.name, data)
      @query_parser.normalize_params(@params, part.name, data)
    end
  end
  MultipartInfo.new @params.to_params_hash, @collector.find_all(&:file?).map(&:body)
end