Kawe Mazidjatari b3a68ed095 Add EABase, EAThread and DirtySDK to R5sdk
DirtySDK (EA's Dirty Sockets library) will be used for the LiveAPI implementation, and depends on: EABase, EAThread.
2024-04-05 18:29:03 +02:00

1431 lines
50 KiB
C

/*H********************************************************************************/
/*!
\File hpack.c
\Description
This module implements a decode/encoder based on the HPACK spec
(https://tools.ietf.org/html/rfc7541). Which is used for encoding/decoding
the HEADERS frame in the HTTP/2 protocol.
\Copyright
Copyright (c) Electronic Arts 2016. ALL RIGHTS RESERVED.
*/
/********************************************************************************H*/
/*** Include files ****************************************************************/
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "DirtySDK/dirtysock/dirtylib.h"
#include "DirtySDK/dirtysock/dirtymem.h"
#include "DirtySDK/proto/protohttputil.h"
#include "DirtySDK/util/hpack.h"
/*** Defines **********************************************************************/
//! size of the static table
#define HPACK_STATIC_TABLE_SIZE (61)
//! size of huffman code table
#define HPACK_HUFFMAN_TABLE_SIZE (256)
//! overhead for a table entry
#define HPACK_TABLE_ENTRY_OVERHEAD (32)
//! controls if we log the dynamic table info
#define HPACK_DYNAMICTABLE_LOGGING (DIRTYCODE_LOGGING && FALSE)
//! flags for different header field types
#define HPACK_INDEXED_HEADER_FIELD (1 << 7)
#define HPACK_LITERAL_HEADER_FIELD (1 << 6)
#define HPACK_DYNAMIC_TABLE_UPDATE (1 << 5)
#define HPACK_LITERAL_HEADER_FIELD_NEVER (1 << 4)
/*** Type Definitions *************************************************************/
//! defines an entry in the dynamic table
typedef struct TableEntryT
{
char *pName; //!< header name
char *pValue; //!< header value
} TableEntryT;
//! defines an entry in the static table
typedef struct StaticTableEntryT
{
const char *pName; //!< header name
const char *pValue; //!< header value
} StaticTableEntryT;
//! linked list node we use to define our dynamic table
typedef struct DynamicNodeT
{
struct DynamicNodeT *pNext;
struct DynamicNodeT *pPrev;
TableEntryT Entry;
} DynamicNodeT;
//! entry in the huffman tree
typedef struct HuffmanCodeT
{
uint32_t uCode; //!< the huffman code
uint32_t uLen; //!< the length of the code in bits
} HuffmanCodeT;
//! tree used for huffman decoding
typedef struct HuffmanNodeT
{
struct HuffmanNodeT *pLeft; //!< 1 bit
struct HuffmanNodeT *pRight; //!< 0 bit
uint8_t uChar; //!< the character represented by the sequence
uint8_t _pad[3];
} HuffmanNodeT;
//! data used to keep track of the huffman encoding
typedef struct HuffmanEncodeT
{
uint32_t aResult[256];
uint32_t uCount;
uint32_t uNextBoundary;
} HuffmanEncodeT;
//! module ref for our encoder/decoder
struct HpackRefT
{
DynamicNodeT *pHead; //!< points to the head of the dynamic table (for insertion/lookup)
DynamicNodeT *pTail; //!< points to the tail of the dynamic table (for removal)
uint32_t uTableSize; //!< current size of the dynamic table
uint32_t uTableMax; //!< maximum size of the dynamic table
HuffmanNodeT *pHuffmanTree; //!< huffman tree used for decoding
//! memgroup data
int32_t iMemGroup;
void *pMemGroupUserData;
uint8_t bUpdatePending; //!< there is a pending update to the dynamic table
uint8_t _pad[3];
};
/*** Variables ********************************************************************/
//! our fixed static table
static const StaticTableEntryT Hpack_aStaticTable[HPACK_STATIC_TABLE_SIZE] =
{
{ ":authority", "" },
{ ":method", "GET" },
{ ":method", "POST" },
{ ":path", "/" },
{ ":path", "/index.html" },
{ ":scheme", "http" },
{ ":scheme", "https" },
{ ":status", "200" },
{ ":status", "204" },
{ ":status", "206" },
{ ":status", "304" },
{ ":status", "400" },
{ ":status", "404" },
{ ":status", "500" },
{ "accept-charset", "" },
{ "accept-encoding", "gzip, deflate" },
{ "accept-language", "" },
{ "accept-ranges", "" },
{ "accept", "" },
{ "access-control-allow-origin", "" },
{ "age", "" },
{ "allow", "" },
{ "authorization", "" },
{ "cache-control", "" },
{ "content-disposition", "" },
{ "content-encoding", "" },
{ "content-language", "" },
{ "content-length", "" },
{ "content-location", "" },
{ "content-range", "" },
{ "content-type", "" },
{ "cookie", "" },
{ "date", "" },
{ "etag", "" },
{ "expect", "" },
{ "expires", "" },
{ "from", "" },
{ "host", "" },
{ "if-match", "" },
{ "if-modified-since", "" },
{ "if-none-match", "" },
{ "if-range", "" },
{ "if-unmodified-since", "" },
{ "last-modified", "" },
{ "link", "" },
{ "location", "" },
{ "max-forwards", "" },
{ "proxy-authenticate", "" },
{ "proxy-authorization", "" },
{ "range", "" },
{ "referer", "" },
{ "refresh", "" },
{ "retry-after", "" },
{ "server", "" },
{ "set-cookie", "" },
{ "strict-transport-security", "" },
{ "transfer-encoding", "" },
{ "user-agent", "" },
{ "vary", "" },
{ "via", "" },
{ "www-authenticate", "" }
};
//! table of huffman codes used for encoding / decoding based on
//! https://tools.ietf.org/html/rfc7541#appendix-B
static const HuffmanCodeT Hpack_aHuffmanCodes[256] =
{
{ 0x00001ff8, 13 }, { 0x007fffd8, 23 }, { 0x0fffffe2, 28 }, { 0x0fffffe3, 28 }, { 0x0fffffe4, 28 }, { 0x0fffffe5, 28 }, { 0x0fffffe6, 28 }, { 0x0fffffe7, 28 }, /* 0 - 7 */
{ 0x0fffffe8, 28 }, { 0x00ffffea, 24 }, { 0x3ffffffc, 30 }, { 0x0fffffe9, 28 }, { 0x0fffffea, 28 }, { 0x3ffffffd, 30 }, { 0x0fffffeb, 28 }, { 0x0fffffec, 28 }, /* 8 - 15 */
{ 0x0fffffed, 28 }, { 0x0fffffee, 28 }, { 0x0fffffef, 28 }, { 0x0ffffff0, 28 }, { 0x0ffffff1, 28 }, { 0x0ffffff2, 28 }, { 0x3ffffffe, 30 }, { 0x0ffffff3, 28 }, /* 16 - 23 */
{ 0x0ffffff4, 28 }, { 0x0ffffff5, 28 }, { 0x0ffffff6, 28 }, { 0x0ffffff7, 28 }, { 0x0ffffff8, 28 }, { 0x0ffffff9, 28 }, { 0x0ffffffa, 28 }, { 0x0ffffffb, 28 }, /* 24 - 31 */
{ 0x00000014, 6 }, { 0x000003f8, 10 }, { 0x000003f9, 10 }, { 0x00000ffa, 12 }, { 0x00001ff9, 13 }, { 0x00000015, 6 }, { 0x000000f8, 8 }, { 0x000007fa, 11 }, /* 32 - 39 */
{ 0x000003fa, 10 }, { 0x000003fb, 10 }, { 0x000000f9, 8 }, { 0x000007fb, 11 }, { 0x000000fa, 8 }, { 0x00000016, 6 }, { 0x00000017, 6 }, { 0x00000018, 6 }, /* 40 - 47 */
{ 0x00000000, 5 }, { 0x00000001, 5 }, { 0x00000002, 5 }, { 0x00000019, 6 }, { 0x0000001a, 6 }, { 0x0000001b, 6 }, { 0x0000001c, 6 }, { 0x0000001d, 6 }, /* 48 - 55 */
{ 0x0000001e, 6 }, { 0x0000001f, 6 }, { 0x0000005c, 7 }, { 0x000000fb, 8 }, { 0x00007ffc, 15 }, { 0x00000020, 6 }, { 0x00000ffb, 12 }, { 0x000003fc, 10 }, /* 56 - 63 */
{ 0x00001ffa, 13 }, { 0x00000021, 6 }, { 0x0000005d, 7 }, { 0x0000005e, 7 }, { 0x0000005f, 7 }, { 0x00000060, 7 }, { 0x00000061, 7 }, { 0x00000062, 7 }, /* 64 - 71 */
{ 0x00000063, 7 }, { 0x00000064, 7 }, { 0x00000065, 7 }, { 0x00000066, 7 }, { 0x00000067, 7 }, { 0x00000068, 7 }, { 0x00000069, 7 }, { 0x0000006a, 7 }, /* 72 - 79 */
{ 0x0000006b, 7 }, { 0x0000006c, 7 }, { 0x0000006d, 7 }, { 0x0000006e, 7 }, { 0x0000006f, 7 }, { 0x00000070, 7 }, { 0x00000071, 7 }, { 0x00000072, 7 }, /* 80 - 87 */
{ 0x000000fc, 8 }, { 0x00000073, 7 }, { 0x000000fd, 8 }, { 0x00001ffb, 13 }, { 0x0007fff0, 19 }, { 0x00001ffc, 13 }, { 0x00003ffc, 14 }, { 0x00000022, 6 }, /* 88 - 95 */
{ 0x00007ffd, 15 }, { 0x00000003, 5 }, { 0x00000023, 6 }, { 0x00000004, 5 }, { 0x00000024, 6 }, { 0x00000005, 5 }, { 0x00000025, 6 }, { 0x00000026, 6 }, /* 96 - 103 */
{ 0x00000027, 6 }, { 0x00000006, 5 }, { 0x00000074, 7 }, { 0x00000075, 7 }, { 0x00000028, 6 }, { 0x00000029, 6 }, { 0x0000002a, 6 }, { 0x00000007, 5 }, /* 104 - 111 */
{ 0x0000002b, 6 }, { 0x00000076, 7 }, { 0x0000002c, 6 }, { 0x00000008, 5 }, { 0x00000009, 5 }, { 0x0000002d, 6 }, { 0x00000077, 7 }, { 0x00000078, 7 }, /* 112 - 119 */
{ 0x00000079, 7 }, { 0x0000007a, 7 }, { 0x0000007b, 7 }, { 0x00007ffe, 15 }, { 0x000007fc, 11 }, { 0x00003ffd, 14 }, { 0x00001ffd, 13 }, { 0x0ffffffc, 28 }, /* 120 - 127 */
{ 0x000fffe6, 20 }, { 0x003fffd2, 22 }, { 0x000fffe7, 20 }, { 0x000fffe8, 20 }, { 0x003fffd3, 22 }, { 0x003fffd4, 22 }, { 0x003fffd5, 22 }, { 0x007fffd9, 23 }, /* 128 - 135 */
{ 0x003fffd6, 22 }, { 0x007fffda, 23 }, { 0x007fffdb, 23 }, { 0x007fffdc, 23 }, { 0x007fffdd, 23 }, { 0x007fffde, 23 }, { 0x00ffffeb, 24 }, { 0x007fffdf, 23 }, /* 136 - 143 */
{ 0x00ffffec, 24 }, { 0x00ffffed, 24 }, { 0x003fffd7, 22 }, { 0x007fffe0, 23 }, { 0x00ffffee, 24 }, { 0x007fffe1, 23 }, { 0x007fffe2, 23 }, { 0x007fffe3, 23 }, /* 144 - 151 */
{ 0x007fffe4, 23 }, { 0x001fffdc, 21 }, { 0x003fffd8, 22 }, { 0x007fffe5, 23 }, { 0x003fffd9, 22 }, { 0x007fffe6, 23 }, { 0x007fffe7, 23 }, { 0x00ffffef, 24 }, /* 152 - 159 */
{ 0x003fffda, 22 }, { 0x001fffdd, 21 }, { 0x000fffe9, 20 }, { 0x003fffdb, 22 }, { 0x003fffdc, 22 }, { 0x007fffe8, 23 }, { 0x007fffe9, 23 }, { 0x001fffde, 21 }, /* 160 - 167 */
{ 0x007fffea, 23 }, { 0x003fffdd, 22 }, { 0x003fffde, 22 }, { 0x00fffff0, 24 }, { 0x001fffdf, 21 }, { 0x003fffdf, 22 }, { 0x007fffeb, 23 }, { 0x007fffec, 23 }, /* 168 - 175 */
{ 0x001fffe0, 21 }, { 0x001fffe1, 21 }, { 0x003fffe0, 22 }, { 0x001fffe2, 21 }, { 0x007fffed, 23 }, { 0x003fffe1, 22 }, { 0x007fffee, 23 }, { 0x007fffef, 23 }, /* 176 - 183 */
{ 0x000fffea, 20 }, { 0x003fffe2, 22 }, { 0x003fffe3, 22 }, { 0x003fffe4, 22 }, { 0x007ffff0, 23 }, { 0x003fffe5, 22 }, { 0x003fffe6, 22 }, { 0x007ffff1, 23 }, /* 184 - 191 */
{ 0x03ffffe0, 26 }, { 0x03ffffe1, 26 }, { 0x000fffeb, 20 }, { 0x0007fff1, 19 }, { 0x003fffe7, 22 }, { 0x007ffff2, 23 }, { 0x003fffe8, 22 }, { 0x01ffffec, 25 }, /* 192 - 199 */
{ 0x03ffffe2, 26 }, { 0x03ffffe3, 26 }, { 0x03ffffe4, 26 }, { 0x07ffffde, 27 }, { 0x07ffffdf, 27 }, { 0x03ffffe5, 26 }, { 0x00fffff1, 24 }, { 0x01ffffed, 25 }, /* 200 - 207 */
{ 0x0007fff2, 19 }, { 0x001fffe3, 21 }, { 0x03ffffe6, 26 }, { 0x07ffffe0, 27 }, { 0x07ffffe1, 27 }, { 0x03ffffe7, 26 }, { 0x07ffffe2, 27 }, { 0x00fffff2, 24 }, /* 208 - 215 */
{ 0x001fffe4, 21 }, { 0x001fffe5, 21 }, { 0x03ffffe8, 26 }, { 0x03ffffe9, 26 }, { 0x0ffffffd, 28 }, { 0x07ffffe3, 27 }, { 0x07ffffe4, 27 }, { 0x07ffffe5, 27 }, /* 216 - 223 */
{ 0x000fffec, 20 }, { 0x00fffff3, 24 }, { 0x000fffed, 20 }, { 0x001fffe6, 21 }, { 0x003fffe9, 22 }, { 0x001fffe7, 21 }, { 0x001fffe8, 21 }, { 0x007ffff3, 23 }, /* 224 - 231 */
{ 0x003fffea, 22 }, { 0x003fffeb, 22 }, { 0x01ffffee, 25 }, { 0x01ffffef, 25 }, { 0x00fffff4, 24 }, { 0x00fffff5, 24 }, { 0x03ffffea, 26 }, { 0x007ffff4, 23 }, /* 232 - 239 */
{ 0x03ffffeb, 26 }, { 0x07ffffe6, 27 }, { 0x03ffffec, 26 }, { 0x03ffffed, 26 }, { 0x07ffffe7, 27 }, { 0x07ffffe8, 27 }, { 0x07ffffe9, 27 }, { 0x07ffffea, 27 }, /* 240 - 247 */
{ 0x07ffffeb, 27 }, { 0x0ffffffe, 28 }, { 0x07ffffec, 27 }, { 0x07ffffed, 27 }, { 0x07ffffee, 27 }, { 0x07ffffef, 27 }, { 0x07fffff0, 27 }, { 0x03ffffee, 26 } /* 248 - 255 */
};
// EOS (End of String) specifier
static const HuffmanCodeT Hpack_EOS = { 0x3fffffff, 30 };
/*** Private Functions ************************************************************/
/*F********************************************************************************/
/*!
\Function _HpackDynamicTableGet
\Description
Attempts to find a table entry in the static and dynamic tables
\Input *pRef - module state
\Input iIndex - the index of the table entry we are looking for
\Output
const TableEntryT * - the found entry or NULL
\Version 10/05/2016 (eesponda)
*/
/********************************************************************************F*/
static const TableEntryT *_HpackDynamicTableGet(HpackRefT *pRef, int32_t iIndex)
{
int32_t iCount;
const DynamicNodeT *pNode;
// loop through the list either finding our index or hitting the end, if index is found return the entry
for (pNode = pRef->pHead, iCount = 0; pNode != NULL; pNode = pNode->pNext, iCount += 1)
{
if (iCount == iIndex)
{
break;
}
}
return(pNode != NULL ? &pNode->Entry : NULL);
}
/*F********************************************************************************/
/*!
\Function _HpackHuffmanTreeInsert
\Description
Inserts a character into the huffman tree recursively
\Input *pRef - module state
\Input **pNode - current node we are visiting
\Input uCode - huffman code
\Input uLen - number of bits in the code
\Input uChar - character corresponding to huffman code
\Output
uint8_t - TRUE if successfully, FALSE otherwise
\Version 10/21/2016 (eesponda)
*/
/********************************************************************************F*/
static uint8_t _HpackHuffmanTreeInsert(HpackRefT *pRef, HuffmanNodeT **pNode, uint32_t uCode, uint32_t uLen, uint8_t uChar)
{
HuffmanNodeT **pTargetNode = ((uCode >> (uLen-1)) & 1) ? &(*pNode)->pLeft : &(*pNode)->pRight;
if (*pTargetNode == NULL)
{
if ((*pTargetNode = (HuffmanNodeT *)DirtyMemAlloc(sizeof(**pTargetNode), HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData)) == NULL)
{
NetPrintf(("hpack: [%p] could not allocate node for huffman tree\n", pRef));
return(FALSE);
}
ds_memclr(*pTargetNode, sizeof(**pTargetNode));
}
if ((uLen-1) > 0)
{
return(_HpackHuffmanTreeInsert(pRef, pTargetNode, uCode, uLen-1, uChar));
}
else
{
(*pTargetNode)->uChar = uChar;
return(TRUE);
}
}
/*F********************************************************************************/
/*!
\Function _HpackHuffmanTreeBuild
\Description
Builds the huffman tree based on the static huffman code table
\Input *pRef - module state
\Output
uint8_t - TRUE if successfully, FALSE otherwise
\Version 10/19/2016 (eesponda)
*/
/********************************************************************************F*/
static uint8_t _HpackHuffmanTreeBuild(HpackRefT *pRef)
{
uint8_t uChar, bResult = TRUE;
// allocate the root of the tree
if ((pRef->pHuffmanTree = (HuffmanNodeT *)DirtyMemAlloc(sizeof(*pRef->pHuffmanTree), HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData)) == NULL)
{
NetPrintf(("hpack: [%p] could not allocate root of huffman tree\n", pRef));
return(FALSE);
}
ds_memclr(pRef->pHuffmanTree, sizeof(*pRef->pHuffmanTree));
/* go through each character one at a time 0-255
building a path in the huffman tree based on the code for the character
at the end save the character in the leaf
if any allocation fails this will be all cleaned up in the destroy that
happens if this function returns FALSE
*/
for (uChar = 0; (uChar < HPACK_HUFFMAN_TABLE_SIZE-1) && (bResult == TRUE); uChar += 1)
{
const HuffmanCodeT *pHuffman = &Hpack_aHuffmanCodes[uChar];
bResult = _HpackHuffmanTreeInsert(pRef, &pRef->pHuffmanTree, pHuffman->uCode, pHuffman->uLen, uChar);
}
return(bResult);
}
/*F********************************************************************************/
/*!
\Function _HpackHuffmanTreeDestroy
\Description
Recursively clean up the huffman tree
\Input *pRef - module state
\Input *pNode - currently visited node to cleanup
\Version 10/19/2016 (eesponda)
*/
/********************************************************************************F*/
static void _HpackHuffmanTreeDestroy(HpackRefT *pRef, HuffmanNodeT *pNode)
{
if (pNode == NULL)
{
return;
}
_HpackHuffmanTreeDestroy(pRef, pNode->pLeft);
pNode->pLeft = NULL;
_HpackHuffmanTreeDestroy(pRef, pNode->pRight);
pNode->pRight = NULL;
DirtyMemFree(pNode, HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData);
}
/*F********************************************************************************/
/*!
\Function _HpackHuffmanTreeDecode
\Description
Use the huffman tree to decode a series of octets
\Input *pRef - module state
\Input *pData - input buffer with octets to decode
\Input iLen - size of the input buffer to decode
\Input *pBuffer - [out] output string that we write data to
\Input *pSuccess- [out] if the decoding was success
\Version 10/19/2016 (eesponda)
*/
/********************************************************************************F*/
static void _HpackHuffmanTreeDecode(HpackRefT *pRef, const uint8_t *pData, int32_t iLen, char **pBuffer, uint8_t *pSuccess)
{
int32_t iIndex, iWrite;
char strTemp[256];
const HuffmanNodeT *pNode = pRef->pHuffmanTree;
/* loop through each octet traversing the tree bit by bit
when a leaf node is found write that to our output */
for (iIndex = 0, iWrite = 0; iIndex < iLen; iIndex += 1)
{
int32_t iBit;
for (iBit = 7; iBit >= 0; iBit -= 1)
{
pNode = ((pData[iIndex] >> iBit) & 1) ? pNode->pLeft : pNode->pRight;
if (pNode == NULL)
{
NetPrintf(("hpack: [%p] huffman code does not exist in tree\n", pRef));
*pSuccess = FALSE;
return;
}
if ((pNode->pLeft != NULL) || (pNode->pRight != NULL))
{
continue;
}
// copy string but leave enough room for null terminator
if ((uint32_t)iWrite < sizeof(strTemp)-1)
{
strTemp[iWrite++] = (char)pNode->uChar;
}
pNode = pRef->pHuffmanTree;
}
}
// write null terminator and return number of bytes written
strTemp[iWrite++] = '\0';
// allocate space for the output buffer
if ((*pBuffer = (char *)DirtyMemAlloc(iWrite, HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData)) != NULL)
{
ds_strnzcpy(*pBuffer, strTemp, iWrite);
}
else
{
NetPrintf(("hpack: [%p] could not allocate space for decoding the string into\n", pRef));
*pSuccess = FALSE;
}
}
/*F********************************************************************************/
/*!
\Function _HpackDecodeInteger
\Description
Decodes an integer from the sequence of octets
\Input *pBuf - where we decode the integer from
\Input iBufLen - size of the input buffer
\Input uMask - enable bits we use for decoding the integer
\Input *pValue - [out] where we write the decoded integer
\Output
int32_t - amount of bytes read from the buffer
\Version 10/07/2016 (eesponda)
*/
/********************************************************************************F*/
static int32_t _HpackDecodeInteger(const uint8_t *pBuf, int32_t iBufLen, uint8_t uMask, uint32_t *pValue)
{
int32_t iRead = 1; //!< we always at least read one byte
if ((*pValue = *pBuf & uMask) == uMask)
{
uint8_t uMSB = 0, uByte;
// read until you decode the full integer or reach the end of the buffer
do
{
uByte = pBuf[iRead++];
*pValue += (uByte & 0x7f) * (1 << uMSB);
uMSB += 7;
} while (((uByte & 0x80) == 0x80) && (iRead < iBufLen));
// log in the case of reaching the end
if (iRead == iBufLen)
{
NetPrintf(("hpack: reached the end of the buffer before decoding full integer\n"));
}
}
return(iRead);
}
/*F********************************************************************************/
/*!
\Function _HpackDecodeString
\Description
Decodes an string from the sequence of octets
\Input *pRef - module state
\Input *pBuf - where we decode the string from
\Input iBufLen - size of the input buffer
\Input *pOutput - [out] where we write the decode string
\Input *pSuccess- [out] if the decode was successful
\Output
int32_t - amount of bytes read from the buffer
\Version 10/07/2016 (eesponda)
*/
/********************************************************************************F*/
static int32_t _HpackDecodeString(HpackRefT *pRef, const uint8_t *pBuf, int32_t iBufLen, char **pOutput, uint8_t *pSuccess)
{
int32_t iRead = 0;
uint8_t bHuffman;
uint32_t uLen;
// initialize the output to NULL in cases of error
*pOutput = NULL;
// decode if huffman encoding was used and the length of the string
bHuffman = (*pBuf & 0x80) == 0x80;
iRead += _HpackDecodeInteger(pBuf, iBufLen, 0x7f, &uLen);
// make sure we have enough space in input
if ((uint32_t)iBufLen-iRead < uLen)
{
NetPrintf(("hpack: [%p] not enough space in input buffer to decode string\n", pRef));
*pSuccess = FALSE;
return(iBufLen-iRead);
}
// decode using the huffman tree or just copy the literal string
if (bHuffman == TRUE)
{
_HpackHuffmanTreeDecode(pRef, pBuf+iRead, uLen, pOutput, pSuccess);
}
else
{
// allocate the buffer for decoding
if ((*pOutput = (char *)DirtyMemAlloc(uLen+1, HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData)) != NULL)
{
ds_strnzcpy(*pOutput, (const char *)pBuf+iRead, uLen+1);
}
else
{
NetPrintf(("hpack: [%p] could not allocate space for decoding the string into\n", pRef));
*pSuccess = FALSE;
}
}
return(iRead+uLen);
}
/*F********************************************************************************/
/*!
\Function _HpackDecodeIndexedField
\Description
Decodes an indexed header field
\Input *pRef - module state
\Input uIndex - index of the header field
\Input *pEntry - [out] header field data retrieved from the table
\Output
uint8_t - TRUE if successfully pulled the indexed field from the table
\Version 10/17/2016 (eesponda)
*/
/********************************************************************************F*/
static uint8_t _HpackDecodeIndexedField(HpackRefT *pRef, uint32_t uIndex, TableEntryT *pEntry)
{
const TableEntryT *pFound;
uint8_t bResult = TRUE;
if (uIndex == 0)
{
NetPrintf(("hpack: [%p] received invalid index of 0 when decoding indexed header field\n", pRef));
return(FALSE);
}
uIndex -= 1; // make it zero-indexed
// try to find the entry in the static table index space
if (uIndex < HPACK_STATIC_TABLE_SIZE)
{
ds_memcpy(pEntry, &Hpack_aStaticTable[uIndex], sizeof(*pEntry));
}
// try to find the entry in the dynamic table index space
else if ((pFound = _HpackDynamicTableGet(pRef, uIndex - HPACK_STATIC_TABLE_SIZE)) != NULL)
{
ds_memcpy(pEntry, pFound, sizeof(*pEntry));
}
else
{
NetPrintf(("hpack: [%p] indexed header field could not be found at index %u\n", pRef, uIndex));
bResult = FALSE;
}
return(bResult);
}
/*F********************************************************************************/
/*!
\Function _HpackFindIndexedField
\Description
Find an index given a header field
\Input *pRef - module state
\Input *pEntry - header field we need the index for
\Input *pIndex - [out] index of the header field
\Output
uint8_t - TRUE if successfully found the index for the header field
\Version 10/21/2016 (eesponda)
*/
/********************************************************************************F*/
static uint8_t _HpackFindIndexedField(HpackRefT *pRef, const StaticTableEntryT *pEntry, uint32_t *pIndex)
{
uint32_t uIndex;
const TableEntryT *pFound;
// try to find a valid index in the static table space
for (uIndex = 0; uIndex < HPACK_STATIC_TABLE_SIZE; uIndex += 1)
{
if (strncmp(pEntry->pName, Hpack_aStaticTable[uIndex].pName, strlen(pEntry->pName)) == 0 &&
strncmp(pEntry->pValue, Hpack_aStaticTable[uIndex].pValue, strlen(pEntry->pValue)) == 0)
{
*pIndex = uIndex+1;
return(TRUE);
}
}
// if not found in static, try the dynamic table space
uIndex = 0;
while ((pFound = _HpackDynamicTableGet(pRef, uIndex)) != NULL)
{
if (strncmp(pEntry->pName, pFound->pName, strlen(pEntry->pName)) == 0 &&
strncmp(pEntry->pValue, pFound->pValue, strlen(pEntry->pValue)) == 0)
{
*pIndex = uIndex+HPACK_STATIC_TABLE_SIZE+1;
return(TRUE);
}
uIndex += 1;
}
// otherwise it was not found
return(FALSE);
}
/*F********************************************************************************/
/*!
\Function _HpackFindIndexedFieldName
\Description
Find an index given a header field name
\Input *pRef - module state
\Input *pName - the header field name we are looking for
\Input *pIndex - [out] index of the header field
\Output
uint8_t - TRUE if successfully found the index for the header field name
\Version 10/21/2016 (eesponda)
*/
/********************************************************************************F*/
static uint8_t _HpackFindIndexedFieldName(HpackRefT *pRef, const char *pName, uint32_t *pIndex)
{
uint32_t uIndex;
const TableEntryT *pFound;
// try to find a valid index in the static table space
for (uIndex = 0; uIndex < HPACK_STATIC_TABLE_SIZE; uIndex += 1)
{
if (strncmp(pName, Hpack_aStaticTable[uIndex].pName, strlen(Hpack_aStaticTable[uIndex].pName)) == 0)
{
*pIndex = uIndex+1;
return(TRUE);
}
}
// if not found in static, try the dynamic table space
uIndex = 0;
while ((pFound = _HpackDynamicTableGet(pRef, uIndex)) != NULL)
{
if (strncmp(pName, pFound->pName, strlen(pFound->pName)) == 0)
{
*pIndex = uIndex+HPACK_STATIC_TABLE_SIZE+1;
return(TRUE);
}
uIndex += 1;
}
// otherwise it was not found
return(FALSE);
}
/*F********************************************************************************/
/*!
\Function _HpackDecodeLiteralField
\Description
Decodes a literal header field
\Input *pRef - module state
\Input *pBuf - bytes we are decoding the header field from
\Input iBufSize - size of the input buffer
\Input uIndex - index of the header field
\Input *pEntry - [out] header field data retrieved from the table
\Input *pSuccess- [out] if the decode was successful
\Output
int32_t - number of bytes read from the buffer
\Version 10/17/2016 (eesponda)
*/
/********************************************************************************F*/
static int32_t _HpackDecodeLiteralField(HpackRefT *pRef, const uint8_t *pBuf, int32_t iBufSize, uint32_t uIndex, TableEntryT *pEntry, uint8_t *pSuccess)
{
int32_t iRead = 0;
// if the header name is indexed then pull it from the table
if (uIndex > 0)
{
char *pName;
int32_t iLen;
// retrieve the entry, the value in pEntry will point to the static table entry
if (_HpackDecodeIndexedField(pRef, uIndex, pEntry) == FALSE)
{
*pSuccess = FALSE;
return(0);
}
// allocate the space for the string as this will go into the dynamic table
iLen = (int32_t)strlen(pEntry->pName)+1;
if ((pName = (char *)DirtyMemAlloc(iLen, HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData)) != NULL)
{
ds_strnzcpy(pName, pEntry->pName, iLen);
}
else
{
NetPrintf(("hpack: [%p] could not allocate space for the entry name\n", pRef));
*pSuccess = FALSE;
}
pEntry->pName = pName;
}
// otherwise decode it from the buffer
else
{
iRead += _HpackDecodeString(pRef, pBuf+iRead, iBufSize-iRead, &pEntry->pName, pSuccess);
}
// decode the value from the buffer
iRead += _HpackDecodeString(pRef, pBuf+iRead, iBufSize-iRead, &pEntry->pValue, pSuccess);
// return how much data was read
return(iRead);
}
/*F********************************************************************************/
/*!
\Function _HpackDynamicTableEject
\Description
Ejects header fields from the dynamic table until size hits a specified
value
\Input *pRef - module state
\Input uSize - size we need the table to be under
\Version 10/17/2016 (eesponda)
*/
/********************************************************************************F*/
static void _HpackDynamicTableEject(HpackRefT *pRef, uint32_t uSize)
{
// if we are over the size, eject entries
while (pRef->uTableSize > uSize)
{
DynamicNodeT *pNode = pRef->pTail;
// recalculate new size
pRef->uTableSize -= (uint32_t)strlen(pNode->Entry.pName) + (uint32_t)strlen(pNode->Entry.pValue) + HPACK_TABLE_ENTRY_OVERHEAD;
// fix up pointers and delete memory
if (pRef->pTail != pRef->pHead)
{
pRef->pTail = pNode->pPrev;
pRef->pTail->pNext = NULL;
}
else
{
pRef->pTail = pRef->pHead = NULL;
}
DirtyMemFree(pNode->Entry.pName, HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData);
DirtyMemFree(pNode->Entry.pValue, HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData);
DirtyMemFree(pNode, HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData);
}
}
/*F********************************************************************************/
/*!
\Function _HpackDynamicTableResize
\Description
Updates the maximum size of the dynamic table
\Input *pRef - module state
\Input uSize - new maximum size of the table
\Version 10/17/2016 (eesponda)
*/
/********************************************************************************F*/
static void _HpackDynamicTableResize(HpackRefT *pRef, uint32_t uSize)
{
// eject entries
_HpackDynamicTableEject(pRef, uSize);
// update size
pRef->uTableMax = uSize;
#if HPACK_DYNAMICTABLE_LOGGING
NetPrintf(("hpack: [%p] new table size maximum %u\n", pRef, pRef->uTableMax));
#endif
}
/*F********************************************************************************/
/*!
\Function _HpackDynamicTableInsert
\Description
Inserts a new entry into the dynamic table, ejecting any necessary entries
\Input *pRef - module state
\Input *pEntry - new entry we are adding to the table
\Output
uint8_t - success=TRUE, failure=FALSE
\Version 10/17/2016 (eesponda)
*/
/********************************************************************************F*/
static uint8_t _HpackDynamicTableInsert(HpackRefT *pRef, const TableEntryT *pEntry)
{
DynamicNodeT *pNode;
uint32_t uEntrySize;
// make sure entry is valid
if ((pEntry->pName == NULL) || (pEntry->pValue == NULL))
{
return(FALSE);
}
// allocate memory for the node in the dynamic table
if ((pNode = (DynamicNodeT *)DirtyMemAlloc(sizeof(*pNode), HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData)) == NULL)
{
return(FALSE);
}
ds_memclr(pNode, sizeof(*pNode));
ds_memcpy(&pNode->Entry, pEntry, sizeof(pNode->Entry));
uEntrySize = (uint32_t)strlen(pEntry->pName) + (uint32_t)strlen(pEntry->pValue) + HPACK_TABLE_ENTRY_OVERHEAD;
pRef->uTableSize += uEntrySize;
// attach to list
if (pRef->pTail != NULL)
{
pNode->pNext = pRef->pHead;
pRef->pHead->pPrev = pNode;
}
else
{
pRef->pTail = pNode;
}
pRef->pHead = pNode;
// if we are over the size, eject entries
_HpackDynamicTableEject(pRef, pRef->uTableMax);
return(TRUE);
}
/*F********************************************************************************/
/*!
\Function _HpackEncodeInteger
\Description
Encodes an integer into a sequence of octets
\Input uValue - integer we are encoding
\Input uMask - enable bits we use for encoding the integer
\Input *pBuf - [out] where we encode the integer into
\Input iBufLen - size of the output buffer
\Output
int32_t - amount of bytes written to the buffer
\Version 10/21/2016 (eesponda)
*/
/********************************************************************************F*/
static int32_t _HpackEncodeInteger(uint32_t uValue, uint8_t uMask, uint8_t *pBuf, int32_t iBufLen)
{
int32_t iWrite = 1;
if (uValue < uMask)
{
*pBuf |= (uint8_t)uValue;
}
else
{
*pBuf |= uMask; //!< turn on all the bits in the prefix
uValue -= uMask; //!< decrease the value by that amount
for (iWrite = 1; (iWrite < iBufLen) && (uValue >= 0x80); iWrite += 1)
{
// set the lsb of the value and set the msb as a continuation flag
pBuf[iWrite] = (uValue % 0x80) + 0x80;
uValue /= 0x80;
}
pBuf[iWrite++] = uValue;
}
return(iWrite);
}
/*F********************************************************************************/
/*!
\Function _HpackHuffmanEncode
\Description
Huffman encodes a character
\Input *pEncode - encoding state
\Input *pHuffman - huffman code information
\Version 10/21/2016 (eesponda)
*/
/********************************************************************************F*/
static void _HpackHuffmanEncode(HuffmanEncodeT *pEncode, const HuffmanCodeT *pHuffman)
{
int32_t iIndex;
const int32_t iBits = sizeof(*pEncode->aResult) * 8;
const int32_t iSize = sizeof(pEncode->aResult) / sizeof(*pEncode->aResult);
// increase the count of bits and track the next octet boundary
pEncode->uCount += pHuffman->uLen;
if (pEncode->uCount > pEncode->uNextBoundary)
{
/* figure out how much we need to increment the boundary by
if the count is on an octet, just move the boundary there
if the count is over an octet, just move to the next octet past count (might be more than 1 octet) */
const uint8_t uDifference = pEncode->uCount - pEncode->uNextBoundary;
if ((uDifference % 8) == 0)
{
pEncode->uNextBoundary = pEncode->uCount;
}
else
{
const uint8_t uNumOctets = (uDifference / 8) + 1;
pEncode->uNextBoundary += 8 * uNumOctets;
}
}
// loop through the bytes backwards shifting by the left
for (iIndex = iSize-1; iIndex > 0; iIndex -= 1)
{
uint32_t uResult;
if ((uResult = (pEncode->aResult[iIndex] << pHuffman->uLen) | (pEncode->aResult[iIndex-1] >> (iBits-pHuffman->uLen))) != 0)
{
pEncode->aResult[iIndex] = uResult;
}
}
// finally encode using the huffman code
*pEncode->aResult = (*pEncode->aResult << pHuffman->uLen) | pHuffman->uCode;
}
/*F********************************************************************************/
/*!
\Function _HpackEncodeString
\Description
Encodes a string into a sequence of octets
\Input *pRef - module state
\Input *pBuf - where we encode the string from
\Input iBufSize - size of the input buffer
\Input *pOutput - [out] where we write the encoded string
\Input iOutSize - size of the output buffer
\Input bHuffman - use huffman encoding?
\Output
int32_t - amount of bytes written into the buffer
\Version 10/21/2016 (eesponda)
*/
/********************************************************************************F*/
static int32_t _HpackEncodeString(HpackRefT *pRef, const char *pBuf, int32_t iBufSize, uint8_t *pOutput, int32_t iOutSize, uint8_t bHuffman)
{
int32_t iWrite = 0;
if (bHuffman == TRUE)
{
int32_t iIndex, iTotalBytes;
uint32_t uLeftover;
HuffmanCodeT Huffman;
HuffmanEncodeT Encode;
ds_memclr(Encode.aResult, sizeof(Encode.aResult));
Encode.uCount = 0;
Encode.uNextBoundary = 8;
// encode each character one at a time
for (iIndex = 0; iIndex < iBufSize; iIndex += 1)
{
uint8_t uChar = pBuf[iIndex];
_HpackHuffmanEncode(&Encode, &Hpack_aHuffmanCodes[uChar]);
}
// finalize the huffman encoding using the EOS code
if ((uLeftover = Encode.uNextBoundary - Encode.uCount) > 0)
{
Huffman.uCode = Hpack_EOS.uCode >> (Hpack_EOS.uLen - uLeftover);
Huffman.uLen = uLeftover;
_HpackHuffmanEncode(&Encode, &Huffman);
}
pOutput[iWrite] = 0x80;
iTotalBytes = Encode.uCount/8;
iWrite += _HpackEncodeInteger(iTotalBytes, 0x7f, pOutput, iOutSize);
/* write the sequence to the output buffer
to ensure we write only as much as needed we calculate
how many bytes we need to write each iteration */
while ((iTotalBytes > 0) && (iWrite < iOutSize))
{
// figure out how many bytes we need to write
int32_t iRemainder = iTotalBytes % sizeof(*Encode.aResult);
// figure out what index into the result we need to read from
iIndex = (iTotalBytes-1) / sizeof(*Encode.aResult);
// if on the boundary set the bytes to equal to the boundary
if (iRemainder == 0)
{
iRemainder = sizeof(*Encode.aResult);
}
// write the correct number of bytes to the output
for (; iRemainder > 0; iRemainder -= 1, iTotalBytes -= 1)
{
pOutput[iWrite++] = (uint8_t)(Encode.aResult[iIndex] >> ((iRemainder-1) * 8));
}
}
}
else
{
iWrite += _HpackEncodeInteger(iBufSize, 0x7f, pOutput, iOutSize);
ds_memcpy_s(pOutput+iWrite, iOutSize-iWrite, pBuf, iBufSize);
iWrite += iBufSize;
}
return(iWrite);
}
#if HPACK_DYNAMICTABLE_LOGGING
/*F********************************************************************************/
/*!
\Function _HpackPrintTable
\Description
Prints the entries in the dynamic table
\Input *pRef - module state
\Version 11/04/2016 (eesponda)
*/
/********************************************************************************F*/
static void _HpackPrintTable(HpackRefT *pRef)
{
const TableEntryT *pEntry;
uint32_t uIndex = 0;
while ((pEntry = _HpackDynamicTableGet(pRef, uIndex)) != NULL)
{
uIndex += 1;
NetPrintf(("hpack: [%p] [%02u] (s = %u) %s: %s\n", pRef, uIndex, strlen(pEntry->pName) + strlen(pEntry->pValue) + HPACK_TABLE_ENTRY_OVERHEAD, pEntry->pName, pEntry->pValue));
}
NetPrintf(("hpack: [%p] table size: %u\n", pRef, pRef->uTableSize));
}
#endif
/*** Public Functions *************************************************************/
/*F********************************************************************************/
/*!
\Function HpackCreate
\Description
Allocate module state and prepare for use
\Input uTableMax - maximum size of the dynamic table
\Input bDecoder - is the ref for a decoder?
\Output
HpackRefT * - pointer to module state, or NULL
\Notes
uTableMax is based upon SETTINGS_HEADER_TABLE_SIZE in the
HTTP/2 header frame
\Version 10/05/2016 (eesponda)
*/
/********************************************************************************F*/
HpackRefT *HpackCreate(uint32_t uTableMax, uint8_t bDecoder)
{
HpackRefT *pRef;
int32_t iMemGroup;
void *pMemGroupUserData;
// query memgroup data
DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData);
// allocate state
if ((pRef = (HpackRefT *)DirtyMemAlloc(sizeof(*pRef), HPACK_MEMID, iMemGroup, pMemGroupUserData)) == NULL)
{
NetPrintf(("hpack: unable to allocate module state\n"));
return(NULL);
}
ds_memclr(pRef, sizeof(*pRef));
pRef->uTableMax = uTableMax;
pRef->iMemGroup = iMemGroup;
pRef->pMemGroupUserData = pMemGroupUserData;
// build huffman tree
if ((bDecoder == TRUE) && (_HpackHuffmanTreeBuild(pRef) == FALSE))
{
HpackDestroy(pRef);
return(NULL);
}
return(pRef);
}
/*F********************************************************************************/
/*!
\Function HpackDestroy
\Description
Destroy the module and release its state
\Input *pRef - module state
\Version 10/05/2016 (eesponda)
*/
/********************************************************************************F*/
void HpackDestroy(HpackRefT *pRef)
{
// clean up the tree recursively
if (pRef->pHuffmanTree != NULL)
{
_HpackHuffmanTreeDestroy(pRef, pRef->pHuffmanTree);
pRef->pHuffmanTree = NULL;
}
// clear the dynamic table
HpackClear(pRef);
DirtyMemFree(pRef, HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData);
}
/*F********************************************************************************/
/*!
\Function HpackDecode
\Description
Decode the byte array into formatted header string
\Input *pRef - module state
\Input *pInput - the bytes we are decoding
\Input iInpSize - number of bytes in the input
\Input *pOutput - [out] formatted header string CRLF delimited
\Input iOutSize - size of the string we are writing to
\Output
int32_t - success=amount of data written to output, failure=negative
\Version 10/17/2016 (eesponda)
*/
/********************************************************************************F*/
int32_t HpackDecode(HpackRefT *pRef, const uint8_t *pInput, int32_t iInpSize, char *pOutput, int32_t iOutSize)
{
int32_t iRead = 0, iWrite = 0;
uint32_t uIndex;
uint8_t bSuccess = TRUE;
// clear the output
ds_memclr(pOutput, iOutSize);
while ((iRead < iInpSize) && (bSuccess == TRUE))
{
TableEntryT Entry;
uint32_t uSize;
uint8_t uByte = pInput[iRead], bWrite = TRUE, bFree = FALSE;
ds_memclr(&Entry, sizeof(Entry));
/* mask out the higher bits to figure out what type of header field it is
then pass a mask of the lower bits to the decode */
// indexed header field '1' 7-bit prefix
if ((uByte & HPACK_INDEXED_HEADER_FIELD) != 0)
{
iRead += _HpackDecodeInteger(pInput+iRead, iInpSize-iRead, HPACK_INDEXED_HEADER_FIELD-1, &uIndex);
bSuccess = _HpackDecodeIndexedField(pRef, uIndex, &Entry);
}
// literal header field with incremental indexing '01' 6-bit prefix
else if ((uByte & HPACK_LITERAL_HEADER_FIELD) != 0)
{
iRead += _HpackDecodeInteger(pInput+iRead, iInpSize-iRead, HPACK_LITERAL_HEADER_FIELD-1, &uIndex);
iRead += _HpackDecodeLiteralField(pRef, pInput+iRead, iInpSize-iRead, uIndex, &Entry, &bSuccess);
// add entry to dynamic table
if (_HpackDynamicTableInsert(pRef, &Entry) == FALSE)
{
bFree = TRUE; /* free the memory */
bSuccess = FALSE; /* set the failure flag */
}
}
// dynamic table size update '001' 5-bit prefix
else if ((uByte & HPACK_DYNAMIC_TABLE_UPDATE) != 0)
{
bWrite = FALSE; /* disable writing headers */
iRead += _HpackDecodeInteger(pInput+iRead, iInpSize-iRead, HPACK_DYNAMIC_TABLE_UPDATE-1, &uSize);
_HpackDynamicTableResize(pRef, uSize);
}
// literal header field never indexed '0001' 4-bit prefix
else if ((uByte & HPACK_LITERAL_HEADER_FIELD_NEVER) != 0)
{
iRead += _HpackDecodeInteger(pInput+iRead, iInpSize-iRead, HPACK_LITERAL_HEADER_FIELD_NEVER-1, &uIndex);
iRead += _HpackDecodeLiteralField(pRef, pInput+iRead, iInpSize-iRead, uIndex, &Entry, &bSuccess);
bFree = TRUE; /* cleanup after it is written */
}
// literal header field without indexing '0000' 4-bit prefix
else
{
iRead += _HpackDecodeInteger(pInput+iRead, iInpSize-iRead, HPACK_LITERAL_HEADER_FIELD_NEVER-1, &uIndex);
iRead += _HpackDecodeLiteralField(pRef, pInput+iRead, iInpSize-iRead, uIndex, &Entry, &bSuccess);
bFree = TRUE; /* cleanup after it is written */
}
// write the header field out if not dynamic table size update
if (bWrite == TRUE)
{
iWrite += ds_snzprintf(pOutput+iWrite, iOutSize-iWrite, "%s: %s\r\n", Entry.pName, Entry.pValue);
}
// if we need to cleanup entry memory do so
if (bFree == TRUE)
{
DirtyMemFree(Entry.pName, HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData);
DirtyMemFree(Entry.pValue, HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData);
}
}
#if HPACK_DYNAMICTABLE_LOGGING
_HpackPrintTable(pRef);
#endif
return(bSuccess == TRUE ? iWrite : -1);
}
/*F********************************************************************************/
/*!
\Function HpackEncode
\Description
Encode the formatted header string into a byte array
\Input *pRef - module state
\Input *pInput - formatted header string CRLF delimited
\Input *pOutput - [out] byte array we are writing into
\Input iOutSize - size of the byte array we are writing to
\Input bHuffman - do we need to huffman encode the strings?
\Output
int32_t - success=amount of data written, failure=negative
\Version 10/17/2016 (eesponda)
*/
/********************************************************************************F*/
int32_t HpackEncode(HpackRefT *pRef, const char *pInput, uint8_t *pOutput, int32_t iOutSize, uint8_t bHuffman)
{
char strName[256], strValue[4*1024];
StaticTableEntryT TempEntry;
int32_t iWrite = 0;
// clear the output
ds_memclr(pOutput, iOutSize);
// if there is a pending dynamic table update, encode that first
if (pRef->bUpdatePending == TRUE)
{
pOutput[iWrite] = HPACK_DYNAMIC_TABLE_UPDATE;
iWrite += _HpackEncodeInteger(pRef->uTableMax, HPACK_DYNAMIC_TABLE_UPDATE-1, pOutput+iWrite, iOutSize-iWrite);
pRef->bUpdatePending = FALSE;
}
// point to the address of the stack variables
TempEntry.pName = strName;
TempEntry.pValue = strValue;
// for each header entry try to encode
while (ProtoHttpGetNextHeader(NULL, pInput, strName, sizeof(strName), strValue, sizeof(strValue), &pInput) == 0)
{
uint32_t uIndex;
TableEntryT Entry;
/* force the strName to lowercase */
for (uIndex = 0; (strName[uIndex] != '\0') && (uIndex < sizeof(strName)); uIndex += 1)
{
if (isupper(strName[uIndex]))
{
strName[uIndex] = tolower(strName[uIndex]);
}
}
/* attempt to find the header entry in the static/dynamic table
if found, then encode it as an indexed header field */
if (_HpackFindIndexedField(pRef, &TempEntry, &uIndex) == TRUE)
{
// write indexed header field '1' followed by index using 7-bit prefix
pOutput[iWrite] = HPACK_INDEXED_HEADER_FIELD;
iWrite += _HpackEncodeInteger(uIndex, HPACK_INDEXED_HEADER_FIELD-1, pOutput+iWrite, iOutSize-iWrite);
}
// otherwise encode it as literal header field with indexing
else
{
int32_t iLen;
/* write literal header field with incremental indexing '01' followed by index (if indexable name) or encoded string (if not)
and encoded string (for value) */
pOutput[iWrite] = HPACK_LITERAL_HEADER_FIELD;
if (_HpackFindIndexedFieldName(pRef, TempEntry.pName, &uIndex) == TRUE)
{
iWrite += _HpackEncodeInteger(uIndex, HPACK_LITERAL_HEADER_FIELD-1, pOutput+iWrite, iOutSize-iWrite);
}
else
{
iWrite += 1;
iWrite += _HpackEncodeString(pRef, TempEntry.pName, (int32_t)strlen(TempEntry.pName), pOutput+iWrite, iOutSize-iWrite, bHuffman);
}
iWrite += _HpackEncodeString(pRef, TempEntry.pValue, (int32_t)strlen(TempEntry.pValue), pOutput+iWrite, iOutSize-iWrite, bHuffman);
// allocate space for the entry name in the dynamic table
iLen = (int32_t)strlen(TempEntry.pName)+1;
if ((Entry.pName = (char *)DirtyMemAlloc(iLen, HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData)) != NULL)
{
ds_strnzcpy(Entry.pName, TempEntry.pName, iLen);
}
else
{
NetPrintf(("hpack: [%p] could not allocate space for entry name for encoding dynamic table\n", pRef));
}
// allocate space for the entry value in the dynamic table
iLen = (int32_t)strlen(TempEntry.pValue)+1;
if ((Entry.pValue = (char *)DirtyMemAlloc(iLen, HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData)) != NULL)
{
ds_strnzcpy(Entry.pValue, TempEntry.pValue, iLen);
}
else
{
NetPrintf(("hpack: [%p] could not allocate space for entry value for encoding dynamic table\n", pRef));
}
// insert the new entry into the dynamic table
if (_HpackDynamicTableInsert(pRef, &Entry) == FALSE)
{
DirtyMemFree(Entry.pName, HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData);
DirtyMemFree(Entry.pValue, HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData);
return(-1);
}
}
}
#if HPACK_DYNAMICTABLE_LOGGING
_HpackPrintTable(pRef);
#endif
return(iWrite);
}
/*F********************************************************************************/
/*!
\Function HpackClear
\Description
Clears the dynamic table
\Input *pRef - module state
\Version 10/31/2016 (eesponda)
*/
/********************************************************************************F*/
void HpackClear(HpackRefT *pRef)
{
DynamicNodeT *pNode;
//! walk the list and clean up the memory
while ((pNode = pRef->pHead) != NULL)
{
pRef->pHead = pNode->pNext;
DirtyMemFree(pNode->Entry.pName, HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData);
DirtyMemFree(pNode->Entry.pValue, HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData);
DirtyMemFree(pNode, HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData);
}
pRef->pTail = NULL;
pRef->uTableSize = 0;
}
/*F********************************************************************************/
/*!
\Function HpackResize
\Description
Resizes the dynamic table
\Input *pRef - module state
\Input uTableSize - new maximum table size
\Input bSendUpdate - do we need to notify peer of dynamic table size change
\Version 11/09/2016 (eesponda)
*/
/********************************************************************************F*/
void HpackResize(HpackRefT *pRef, uint32_t uTableSize, uint8_t bSendUpdate)
{
_HpackDynamicTableResize(pRef, uTableSize);
pRef->bUpdatePending = bSendUpdate;
}