Class: Protocol::Multipart::FormData::Parser

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

Overview

A configurable parser for multipart/form-data bodies.

Constant Summary collapse

CONTENT_TYPE =
"multipart/form-data"

Instance Method Summary collapse

Constructor Details

#initialize(maximum_field_size: MAXIMUM_FIELD_SIZE, maximum_upload_size: MAXIMUM_UPLOAD_SIZE, maximum_total_size: MAXIMUM_TOTAL_SIZE, maximum_depth: Protocol::URL::FormData::Nested::MAXIMUM_DEPTH, **options) ⇒ Parser

Initialize the form data parser.



23
24
25
26
27
28
29
30
31
32
33
# File 'lib/protocol/multipart/form_data/parser.rb', line 23

def initialize(maximum_field_size: MAXIMUM_FIELD_SIZE, maximum_upload_size: MAXIMUM_UPLOAD_SIZE, maximum_total_size: MAXIMUM_TOTAL_SIZE, maximum_depth: Protocol::URL::FormData::Nested::MAXIMUM_DEPTH, **options)
	if maximum_depth and maximum_depth < 0
		raise ArgumentError, "Form data limits must be non-negative!"
	end
	
	@maximum_field_size = maximum_field_size
	@maximum_upload_size = maximum_upload_size
	@maximum_total_size = maximum_total_size
	@maximum_depth = maximum_depth
	@options = options
end

Instance Method Details

#each(readable, boundary:) ⇒ Object

Incrementally enumerate multipart form data.

Fields are yielded as strings. File uploads are yielded as streaming Upload instances and are only readable during the corresponding block invocation.



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/protocol/multipart/form_data/parser.rb', line 66

def each(readable, boundary:)
	return to_enum(__method__, readable, boundary:) unless block_given?
	
	total_limit = ByteLimit.new(@maximum_total_size, name: :total_size)
	parser = Multipart::Parser.new(readable, boundary, **@options)
	
	parser.each do |part|
		disposition = part.headers["content-disposition"]
		
		unless disposition&.type == "form-data" and name = disposition["name"]
			raise ArgumentError, "Multipart form part is missing a form-data name!"
		end
		
		if filename = disposition["filename"]
			upload = Upload.new(part, filename, @maximum_upload_size, total_limit)
			yield name, upload
			upload.discard
		else
			field_limit = ByteLimit.new(@maximum_field_size, name: :field_size)
			value = String.new.b
			
			part.each do |chunk|
				field_limit.consume(chunk.bytesize)
				total_limit.consume(chunk.bytesize)
				value << chunk
			end
			
			yield name, value
		end
	end
end

#parse(readable, result = make_result, boundary:) ⇒ Object

Parse multipart form data into a nested hash.

When a block is given, each value is passed through the block before assignment. The value returned by the block is assigned to the result. Uploads require a block because they must be consumed before parsing advances to the next part.



44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/protocol/multipart/form_data/parser.rb', line 44

def parse(readable, result = make_result, boundary:)
	each(readable, boundary:) do |name, value|
		if block_given?
			value = yield(name, value)
		elsif value.is_a?(Upload)
			raise ArgumentError, "A block is required to consume file uploads!"
		end
		
		result.add(name, value)
	end
	
	return result.to_h
end