class Parser
A parser for multipart data based on RFC 2046 and RFC 2387. Parses multipart bodies and provides an enumerable interface to access the parts.
Nested Classes and Modules
class PartRepresents a single part within a multipart message.
Definitions
PREAMBLE_SIZE_LIMIT = 64 * 1024
The preamble size limit.
HEADER_SIZE_LIMIT = 64 * 1024
The header size limit for each part.
HEADER_COUNT_LIMIT = 64
The header count limit for each part.
PART_COUNT_LIMIT = 128
The part count limit.
def initialize(readable, boundary, preamble_size_limit: PREAMBLE_SIZE_LIMIT, header_size_limit: HEADER_SIZE_LIMIT, header_count_limit: HEADER_COUNT_LIMIT, part_count_limit: PART_COUNT_LIMIT)
Initialize a new multipart parser.
Signature
-
parameter
readableIO, IO::Stream The readable stream containing multipart data.
-
parameter
boundaryString The boundary string that separates the parts.
-
parameter
preamble_size_limitInteger | Nil The preamble size limit, or nil for no limit.
-
parameter
header_size_limitInteger | Nil The header size limit per part, or nil for no limit.
-
parameter
header_count_limitInteger | Nil The header count limit per part, or nil for no limit.
-
parameter
part_count_limitInteger | Nil The part count limit, or nil for no limit.
Implementation
def initialize(readable, boundary, preamble_size_limit: PREAMBLE_SIZE_LIMIT, header_size_limit: HEADER_SIZE_LIMIT, header_count_limit: HEADER_COUNT_LIMIT, part_count_limit: PART_COUNT_LIMIT)
limits = [preamble_size_limit, header_size_limit, header_count_limit, part_count_limit]
if limits.any?{|limit| limit and limit < 0}
raise ArgumentError, "Multipart limits must be non-negative!"
end
@readable = IO::Stream(readable)
@boundary = boundary
@preamble_size_limit = preamble_size_limit
@header_size_limit = header_size_limit
@header_count_limit = header_count_limit
@part_count_limit = part_count_limit
@boundary_marker = "--#{@boundary}\r\n".freeze
end
def each
Enumerate through each part in the multipart data. Yields each part for processing. If no block is given, returns an enumerator.
Signature
-
returns
Enumerator, Boolean An enumerator if no block given, or true when complete.
Implementation
def each
return to_enum unless block_given?
preamble_size = 0
# Read lines until we find the first boundary:
while true
if line = read_line(preamble_size, @preamble_size_limit, allowance: @boundary_marker.bytesize, chomp: false)
if line == @boundary_marker
break
else
preamble_size += line.bytesize
check_limit(:preamble_size, preamble_size, @preamble_size_limit)
end
else
# End of stream reached without finding boundary:
raise EOFError, "No multipart boundary found in stream!"
end
end
part_count = 0
while true
part_count += 1
check_limit(:part_count, part_count, @part_count_limit)
part = read_part
break unless part
if part.read_empty_boundary?
else
yield part
# Advance to the next boundary after the consumer returns normally. If the consumer raises, stop parsing without draining the request body.
part.discard
end
# Check if this was the last part:
break if part.closing_boundary?
end
return true
end