Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions lib/protocol/url/form_data/nested.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ module URL
module FormData
# Builds nested form data from names and values.
class Nested
# The default maximum depth of a bracketed form name.
MAXIMUM_DEPTH = 8
# The bracketed form name depth limit.
DEPTH_LIMIT = 8

# Initialize the nested form data.
# @parameter maximum_depth [Integer | Nil] The maximum depth of a bracketed form name.
def initialize(maximum_depth: MAXIMUM_DEPTH)
@maximum_depth = maximum_depth
# @parameter depth_limit [Integer | Nil] The bracketed form name depth limit.
def initialize(depth_limit: DEPTH_LIMIT)
@depth_limit = depth_limit
@root = {}
end

Expand All @@ -31,8 +31,8 @@ def add(name, value)
raise ArgumentError, "Invalid form data name: #{name.inspect}!"
end

if @maximum_depth and keys.size > @maximum_depth
raise RangeError, "Form data depth exceeded limit of #{@maximum_depth}!"
if @depth_limit and keys.size > @depth_limit
raise RangeError, "Form data depth exceeded limit of #{@depth_limit}!"
end

Encoding.assign(keys, value, @root)
Expand Down
40 changes: 20 additions & 20 deletions lib/protocol/url/form_data/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,20 @@ module FormData
class Parser
CONTENT_TYPE = "application/x-www-form-urlencoded"

# The default maximum encoded body size.
MAXIMUM_TOTAL_SIZE = 2 * 1024 * 1024
# The encoded body size limit.
SIZE_LIMIT = 2 * 1024 * 1024

# The default maximum number of form pairs.
MAXIMUM_PAIR_COUNT = 1024
# The form pair count limit.
PAIR_COUNT_LIMIT = 1024

# Initialize the form data parser.
# @parameter maximum_total_size [Integer | Nil] The maximum encoded body size.
# @parameter maximum_pair_count [Integer | Nil] The maximum number of form pairs.
# @parameter maximum_depth [Integer | Nil] The maximum depth of a bracketed form name.
def initialize(maximum_total_size: MAXIMUM_TOTAL_SIZE, maximum_pair_count: MAXIMUM_PAIR_COUNT, maximum_depth: Nested::MAXIMUM_DEPTH)
@maximum_total_size = maximum_total_size
@maximum_pair_count = maximum_pair_count
@maximum_depth = maximum_depth
# @parameter size_limit [Integer | Nil] The encoded body size limit.
# @parameter pair_count_limit [Integer | Nil] The form pair count limit.
# @parameter depth_limit [Integer | Nil] The bracketed form name depth limit.
def initialize(size_limit: SIZE_LIMIT, pair_count_limit: PAIR_COUNT_LIMIT, depth_limit: Nested::DEPTH_LIMIT)
@size_limit = size_limit
@pair_count_limit = pair_count_limit
@depth_limit = depth_limit
end

# Parse URL-encoded form data into a nested hash.
Expand Down Expand Up @@ -54,14 +54,14 @@ def each(body)
return to_enum(__method__, body) unless block_given?

buffer = String.new.b
total_size = 0
size = 0
pair_count = 0

while chunk = body.read
break if chunk.empty?

total_size += chunk.bytesize
check_limit(:total_size, total_size, @maximum_total_size)
size += chunk.bytesize
check_limit(:size, size, @size_limit)
buffer << chunk

while separator = buffer.index("&")
Expand All @@ -70,15 +70,15 @@ def each(body)

unless assignment.empty?
pair_count += 1
check_limit(:pair_count, pair_count, @maximum_pair_count)
check_limit(:pair_count, pair_count, @pair_count_limit)
yield_pair(assignment) {|name, value| yield name, value}
end
end
end

unless buffer.empty?
pair_count += 1
check_limit(:pair_count, pair_count, @maximum_pair_count)
check_limit(:pair_count, pair_count, @pair_count_limit)
yield_pair(buffer) {|name, value| yield name, value}
end

Expand All @@ -88,7 +88,7 @@ def each(body)
private

def make_result
return Nested.new(maximum_depth: @maximum_depth)
return Nested.new(depth_limit: @depth_limit)
end

def yield_pair(assignment)
Expand All @@ -105,9 +105,9 @@ def decode_component(component)
return Encoding.unescape(component.tr("+", " "))
end

def check_limit(name, value, maximum)
if maximum and value > maximum
raise RangeError, "Form data #{name} exceeded limit of #{maximum}!"
def check_limit(name, value, limit)
if limit and value > limit
raise RangeError, "Form data #{name} exceeded limit of #{limit}!"
end
end
end
Expand Down
4 changes: 4 additions & 0 deletions releases.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Releases

## Unreleased

- Use consistent limit naming for form data parser constraints.

## v0.7.0

- Allow `Protocol::URL::FormData::Parser#parse` to populate a supplied result object.
Expand Down
4 changes: 2 additions & 2 deletions test/protocol/url/form_data/nested.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,15 @@
end

it "limits nested names" do
nested = subject.new(maximum_depth: 2)
nested = subject.new(depth_limit: 2)

expect do
nested.add("a[b][c]", "value")
end.to raise_exception(RangeError, message: be =~ /depth exceeded/)
end

it "allows the nesting limit to be disabled" do
nested = subject.new(maximum_depth: nil)
nested = subject.new(depth_limit: nil)
nested.add("a[b][c]", "value")

expect(nested.to_h).to be == {"a" => {"b" => {"c" => "value"}}}
Expand Down
10 changes: 5 additions & 5 deletions test/protocol/url/form_data/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,29 +74,29 @@ def to_h
end

it "limits the total encoded size" do
parser = subject.new(maximum_total_size: 4)
parser = subject.new(size_limit: 4)

expect do
parser.each(StringIO.new("name=Samuel")).to_a
end.to raise_exception(RangeError, message: be =~ /total_size exceeded/)
end.to raise_exception(RangeError, message: be =~ /size exceeded/)
end

it "limits the number of pairs" do
parser = subject.new(maximum_pair_count: 1)
parser = subject.new(pair_count_limit: 1)

expect do
parser.each(StringIO.new("a=1&b=2")).to_a
end.to raise_exception(RangeError, message: be =~ /pair_count exceeded/)
end

it "allows limits to be disabled" do
parser = subject.new(maximum_total_size: nil, maximum_pair_count: nil)
parser = subject.new(size_limit: nil, pair_count_limit: nil)

expect(parser.each(StringIO.new("a=1&b=2")).to_a).to be == [["a", "1"], ["b", "2"]]
end

it "limits nested form names" do
parser = subject.new(maximum_depth: 2)
parser = subject.new(depth_limit: 2)

expect do
parser.parse(StringIO.new("a[b][c]=value"))
Expand Down
Loading