class Amber::Router::Parsers::ParamBudget

Overview

Enforces Amber::Configuration::ParamsConfig while one request's parameters are parsed.

Create one budget per request and pass it to every parsing step for that request: the parameter count and the array padding are cumulative. Each check raises Amber::Exceptions::ParameterLimitExceeded, which Amber answers with 400 Bad Request.

budget = Amber::Router::Parsers::ParamBudget.new
budget.admit!("items[3]", "x")
index = budget.reserve_array_slot!(list_of_items, "3")
list_of_items[index] = JSON::Any.new("x")

Defined in:

amber/router/parsers/param_budget.cr

Constructors

Instance Method Summary

Constructor Detail

def self.new(limits : Amber::Configuration::ParamsConfig = Amber.settings.params) #

[View source]

Instance Method Detail

def admit!(key : String, value : String | Nil = nil) : Nil #

Records one parameter and checks its name and value: the name's length and nesting depth, and the value's length. Every query, form and multipart pair passes through here before any reader sees it. Bracket indices are checked where a parser reserves an array slot (#reserve_array_slot!), so a field named by record id, such as users[1700000][name], stays an ordinary parameter.


[View source]
def check_depth!(segment_count : Int32) : Nil #

Checks the number of segments in one key, such as 3 for a[b][c].


[View source]
def check_key!(key : String) : Nil #

[View source]
def check_key_shape!(key : String) : Nil #

Checks a key's length and nesting depth without splitting it, so a hostile key is rejected before any segment array is built.


[View source]
def check_segment!(segment : String) : Nil #

Rejects a bracketed key segment written as a negative integer, such as the -1 in items[-1]. Callers pass only bracketed segments.


[View source]
def check_value!(value : String) : Nil #

[View source]
def each_form_pair(io : IO, & : String, String -> ) : Nil #

Yields each decoded pair of a URL-encoded stream after admitting it.

FormScanner reads the stream in small blocks, never past the remaining max_form_body_bytes allowance, and rejects a raw key or value as soon as it grows past what its limit could decode to.


[View source]

[View source]
def padding_used : Int32 #

[View source]
def param_count : Int32 #

[View source]
def parse_form(io : IO) : HTTP::Params #

Decodes a URL-encoded stream into HTTP::Params, checking every pair.


[View source]
def read_bounded(io : IO, max_bytes : Int, limit_name : String) : String #

Reads at most max_bytes from io, raising when more remain.

Each block is read into a fixed scratch buffer and checked against the remaining allowance before it is kept, so an over-limit body never grows the result past the limit.


[View source]
def read_document(io : IO | Nil) : String #

Reads a JSON or other document body, bounded by max_document_body_bytes.


[View source]
def read_file(io : IO) : String #

Reads one uploaded file into memory, bounded by max_file_bytes.


[View source]
def read_value(io : IO) : String #

Reads one field value, bounded by max_value_bytes.


[View source]
def reserve_array_slot!(array : Array(::JSON::Any), digits : String) : Int32 #

Converts the digits of a bracket index and makes room for it in array.

Returns the index, now within array.size. The index must be at most max_array_index and at most max_array_index_gap past the end of the array, and the empty entries added to reach it count toward max_array_padding.


[View source]
def skip_bounded(io : IO, max_bytes : Int, limit_name : String) : Nil #

Reads and discards at most max_bytes from io through a fixed scratch buffer, raising when more remain.


[View source]