Class: Protocol::Multipart::Parser

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

Overview

A parser for multipart data based on RFC 2046 and RFC 2387. Parses multipart bodies and provides an enumerable interface to access the parts.

Defined Under Namespace

Classes: Part

Constant Summary collapse

MAXIMUM_PREAMBLE_SIZE =

The default maximum number of preamble bytes before the first boundary.

64 * 1024
MAXIMUM_HEADER_SIZE =

The default maximum number of header bytes in each part.

64 * 1024
MAXIMUM_HEADER_COUNT =

The default maximum number of headers in each part.

64
MAXIMUM_PART_COUNT =

The default maximum number of parts.

128

Instance Method Summary collapse

Constructor Details

#initialize(readable, boundary, maximum_preamble_size: MAXIMUM_PREAMBLE_SIZE, maximum_header_size: MAXIMUM_HEADER_SIZE, maximum_header_count: MAXIMUM_HEADER_COUNT, maximum_part_count: MAXIMUM_PART_COUNT) ⇒ Parser

Initialize a new multipart parser.



170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/protocol/multipart/parser.rb', line 170

def initialize(readable, boundary, maximum_preamble_size: MAXIMUM_PREAMBLE_SIZE, maximum_header_size: MAXIMUM_HEADER_SIZE, maximum_header_count: MAXIMUM_HEADER_COUNT, maximum_part_count: MAXIMUM_PART_COUNT)
	limits = [maximum_preamble_size, maximum_header_size, maximum_header_count, maximum_part_count]
	
	if limits.any?{|limit| limit and limit < 0}
		raise ArgumentError, "Multipart limits must be non-negative!"
	end
	
	@readable = IO::Stream(readable)
	@boundary = boundary
	@maximum_preamble_size = maximum_preamble_size
	@maximum_header_size = maximum_header_size
	@maximum_header_count = maximum_header_count
	@maximum_part_count = maximum_part_count
	
	@boundary_marker = "--#{@boundary}\r\n".freeze
end

Instance Method Details

#eachObject

Enumerate through each part in the multipart data. Yields each part for processing. If no block is given, returns an enumerator.



191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/protocol/multipart/parser.rb', line 191

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, @maximum_preamble_size, allowance: @boundary_marker.bytesize, chomp: false)
			if line == @boundary_marker
				break
			else
				preamble_size += line.bytesize
				check_limit(:preamble_size, preamble_size, @maximum_preamble_size)
			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, @maximum_part_count)
		
		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