Class HttpHeaderParser

  • 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.

    • Constructor Detail

      • HttpHeaderParser

        public HttpHeaderParser()
    • Method Detail

      • defaultIllegalHeaderHandler

        public static scala.Function1<ErrorInfo,​scala.runtime.BoxedUnit> defaultIllegalHeaderHandler​(HttpHeaderParser.Settings settings,
                                                                                                           akka.event.LoggingAdapter log)
      • insert

        public static void insert​(HttpHeaderParser parser,
                                  akka.util.ByteString input,
                                  java.lang.Object value)
      • insertRemainingCharsAsNewNodes

        public static void insertRemainingCharsAsNewNodes​(HttpHeaderParser parser,
                                                          akka.util.ByteString input,
                                                          java.lang.Object value)
      • fail

        public static scala.runtime.Nothing$ fail​(java.lang.String summary,
                                                  StatusCode status)
      • log

        public akka.event.LoggingAdapter log()
      • resultHeader

        public HttpHeader resultHeader()
        Contains the parsed header instance after a call to parseHeaderLine.
      • isEmpty

        public boolean isEmpty()
      • createShallowCopy

        public HttpHeaderParser createShallowCopy()
        Returns a copy of this parser that shares the trie data with this instance.
      • parseHeaderLine

        public int parseHeaderLine​(akka.util.ByteString input,
                                   int lineStart,
                                   int cursor,
                                   int nodeIx)
        Parses a header line and returns the line start index of the subsequent line. The parsed header instance is written to the 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)
      • formatTrie

        public java.lang.String formatTrie()
        Renders the trie structure into an ASCII representation.
      • contentHistogram

        public scala.collection.immutable.Map<java.lang.String,​java.lang.Object> contentHistogram()
        Returns the number of header values stored per header type.
      • formatRawTrie

        public java.lang.String formatRawTrie()
        Returns a string representation of the raw trie data.
      • formatSizes

        public java.lang.String formatSizes()
        Returns a string representation of the trie structure size.