public final class HttpHeaderParser
extends java.lang.Object
Provides for time- and space-efficient parsing of an HTTP header line in an HTTP message. It keeps a cache of all headers encountered in a previous request, so as to avoid reparsing and recreation of header model instances. For the life-time of one HTTP connection an instance of this class is owned by the connection, i.e. not shared with other connections. After the connection is closed it may be used by subsequent connections.
The core of this parser/cache is a mutable space-efficient ternary trie (prefix tree) structure, whose data are split across three arrays. The tree supports node addition and update, but no deletion (i.e. we never remove entries).
The nodes
array keeps the main data of the trie nodes. One node is formed of a char (2 bytes) of which the
LSB (least significant byte) is an ASCII octet and the MSB either 0 or an index into the branchData
array.
There are three types of nodes:
1. Simple nodes: non-branching, non-leaf nodes (the most frequent kind of node) have an MSB of zero and exactly
one logical sub-node which is the next node in the nodes
array (i.e. the one at index n + 1).
2. Branching nodes: have 2 or three children and a non-zero MSB. (MSB value - 1)*3 is the row index into the
branchData
array, which contains the branching data.
3. Leaf nodes: have no sub-nodes and no character value. They merely "stop" the node chain and point to a value.
The LSB of leaf nodes is zero and the (MSB value - 1) is an index into the values array.
This design has the following consequences: - Since only leaf nodes can have values the trie cannot store keys that are prefixes of other stored keys. - If the trie stores n values it has less than n branching nodes (adding the first value does not create a branching node, the addition of every subsequent value creates at most one additional branching node). - If the trie has n branching nodes it stores at least n * 2 and at most n * 3 values.
The branchData
array keeps the branching data for branching nodes in the trie.
It's a flattened two-dimensional array with a row consisting of the following 3 signed 16-bit integers:
row-index + 0: if non-zero: index into the nodes
array for the "lesser" child of the node
row-index + 1: if non-zero: index into the nodes
array for the "equal" child of the node
row-index + 2: if non-zero: index into the nodes
array for the "greater" child of the node
The array has a fixed size of 254 rows (since we can address at most 256 - 1 values with the node MSB and
we always have fewer branching nodes than values).
The values
array contains the payload data addressed by trie leaf nodes.
Since we address them via the nodes MSB and zero is reserved the trie
cannot hold more then 255 items, so this array has a fixed size of 255.
Modifier and Type | Class and Description |
---|---|
static class |
HttpHeaderParser.HeaderValueParser |
static class |
HttpHeaderParser.ModeledHeaderValueParser |
static class |
HttpHeaderParser.RawHeaderValueParser |
static class |
HttpHeaderParser.Settings
INTERNAL API
|
Constructor and Description |
---|
HttpHeaderParser() |
Modifier and Type | Method and Description |
---|---|
static HttpHeaderParser |
apply(HttpHeaderParser.Settings settings,
org.apache.pekko.event.LoggingAdapter log) |
scala.collection.immutable.Map<java.lang.String,java.lang.Object> |
contentHistogram()
Returns the number of header values stored per header type.
|
HttpHeaderParser |
createShallowCopy()
Returns a copy of this parser that shares the trie data with this instance.
|
static scala.Function1<ErrorInfo,scala.runtime.BoxedUnit> |
defaultIllegalHeaderHandler(HttpHeaderParser.Settings settings,
org.apache.pekko.event.LoggingAdapter log) |
static scala.runtime.Nothing$ |
fail(java.lang.String summary,
StatusCode status) |
java.lang.String |
formatRawTrie()
Returns a string representation of the raw trie data.
|
java.lang.String |
formatSizes()
Returns a string representation of the trie structure size.
|
java.lang.String |
formatTrie()
Renders the trie structure into an ASCII representation.
|
static void |
insert(HttpHeaderParser parser,
org.apache.pekko.util.ByteString input,
java.lang.Object value) |
static void |
insertRemainingCharsAsNewNodes(HttpHeaderParser parser,
org.apache.pekko.util.ByteString input,
java.lang.Object value) |
boolean |
isEmpty() |
org.apache.pekko.event.LoggingAdapter |
log() |
int |
parseHeaderLine(org.apache.pekko.util.ByteString input,
int lineStart,
int cursor,
int nodeIx)
Parses a header line and returns the line start index of the subsequent line.
|
static HttpHeaderParser |
prime(HttpHeaderParser parser) |
HttpHeader |
resultHeader()
Contains the parsed header instance after a call to
parseHeaderLine . |
HttpHeaderParser.Settings |
settings() |
static char |
toLowerCase(char c)
Efficiently lower-cases the given character.
|
static HttpHeaderParser |
unprimed(HttpHeaderParser.Settings settings,
org.apache.pekko.event.LoggingAdapter log,
scala.Function1<ErrorInfo,scala.runtime.BoxedUnit> warnOnIllegalHeader) |
public static HttpHeaderParser apply(HttpHeaderParser.Settings settings, org.apache.pekko.event.LoggingAdapter log)
public static scala.Function1<ErrorInfo,scala.runtime.BoxedUnit> defaultIllegalHeaderHandler(HttpHeaderParser.Settings settings, org.apache.pekko.event.LoggingAdapter log)
public static HttpHeaderParser unprimed(HttpHeaderParser.Settings settings, org.apache.pekko.event.LoggingAdapter log, scala.Function1<ErrorInfo,scala.runtime.BoxedUnit> warnOnIllegalHeader)
public static HttpHeaderParser prime(HttpHeaderParser parser)
public static void insert(HttpHeaderParser parser, org.apache.pekko.util.ByteString input, java.lang.Object value)
public static void insertRemainingCharsAsNewNodes(HttpHeaderParser parser, org.apache.pekko.util.ByteString input, java.lang.Object value)
public static scala.runtime.Nothing$ fail(java.lang.String summary, StatusCode status)
public static char toLowerCase(char c)
c
- (undocumented)public HttpHeaderParser.Settings settings()
public org.apache.pekko.event.LoggingAdapter log()
public HttpHeader resultHeader()
parseHeaderLine
.public boolean isEmpty()
public HttpHeaderParser createShallowCopy()
public int parseHeaderLine(org.apache.pekko.util.ByteString input, int lineStart, int cursor, int nodeIx)
resultHeader
member.
If the trie still has space for this type of header (and as a whole) the parsed header is cached.
Throws a NotEnoughDataException
if the given input doesn't contain enough data to fully parse the given header
line. Note that there must be at least one byte available after a CRLF sequence, in order to distinguish a true
line ending from a line fold.
If the header is invalid a respective ParsingException
is thrown.input
- (undocumented)lineStart
- (undocumented)cursor
- (undocumented)nodeIx
- (undocumented)public java.lang.String formatTrie()
public scala.collection.immutable.Map<java.lang.String,java.lang.Object> contentHistogram()
public java.lang.String formatRawTrie()
public java.lang.String formatSizes()