1
0
mirror of https://github.com/stefanocasazza/ULib.git synced 2025-09-28 19:05:55 +08:00
ULib/include/ulib/utility/http2.h
stefanocasazza c233caf07a sync
2019-03-01 17:40:11 +01:00

856 lines
26 KiB
C++

// ============================================================================
//
// = LIBRARY
// ULib - c++ library
//
// = FILENAME
// http2.h - HTTP/2 utility
//
// = AUTHOR
// Stefano Casazza
//
// ============================================================================
#ifndef ULIB_HTTP2_H
#define ULIB_HTTP2_H 1
#include <ulib/net/ipaddress.h>
#include <ulib/utility/uhttp.h>
#include <ulib/utility/hexdump.h>
#ifdef USE_LOAD_BALANCE
# include <ulib/net/client/http.h>
#endif
#define HTTP2_FRAME_HEADER_SIZE 9 // The number of bytes of the frame header
#define HTTP2_DEFAULT_WINDOW_SIZE 65535
#define HTTP2_HEADER_TABLE_OFFSET 62
#define HTTP2_MAX_CONCURRENT_STREAMS 128
#define HTTP2_HEADER_TABLE_ENTRY_SIZE_OFFSET 32
#define HTTP2_CONNECTION_PREFACE "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" // (24 bytes)
class UHTTP;
class UClientImage_Base;
class U_EXPORT UHTTP2 {
public:
enum ConnectionState {
CONN_STATE_IDLE = 0x000,
CONN_STATE_OPEN = 0x001,
CONN_STATE_IS_CLOSING = 0x002
};
enum StreamState {
STREAM_STATE_IDLE = 0x000,
STREAM_STATE_OPEN = 0x001,
STREAM_STATE_HALF_CLOSED = 0x002,
STREAM_STATE_CLOSED = 0x004,
STREAM_STATE_RESERVED = 0x008
};
enum SettingsId {
HEADER_TABLE_SIZE = 1,
ENABLE_PUSH = 2,
MAX_CONCURRENT_STREAMS = 3,
INITIAL_WINDOW_SIZE = 4,
MAX_FRAME_SIZE = 5,
MAX_HEADER_LIST_SIZE = 6
};
struct Settings {
uint32_t header_table_size,
enable_push,
max_concurrent_streams,
initial_window_size,
max_frame_size,
max_header_list_size;
};
struct HpackHeaderTableEntry {
UStringRep* name;
UStringRep* value;
};
struct HpackDynamicTable { // (Last In First Out)
uint32_t num_entries,
entry_capacity,
entry_start_index;
// size and capacities are 32+name_len+value_len (as defined by hpack spec)
uint32_t hpack_size,
hpack_capacity, // the value set by SETTINGS_HEADER_TABLE_SIZE _and_ dynamic table size update
hpack_max_capacity; // the value set by SETTINGS_HEADER_TABLE_SIZE
HpackHeaderTableEntry* entries; // ring buffer
};
struct Stream {
UString headers, body;
uint32_t id, state, clength;
};
class Connection {
public:
// Check for memory error
U_MEMORY_TEST
// Allocator e Deallocator
U_MEMORY_ALLOCATOR
U_MEMORY_DEALLOCATOR
int32_t inp_window, // receive flow control window (its size is a measure of the buffering capacity of the sender)
out_window; // send flow control window (its size is a measure of the buffering capacity of the receiver)
ConnectionState state; // state
Settings peer_settings; // settings
UHashMap<UString> itable; // headers request
HpackDynamicTable idyntbl, odyntbl; // hpack dynamic table (request, response)
// streams
Stream streams[HTTP2_MAX_CONCURRENT_STREAMS];
const char* bug_client;
uint32_t max_processed_stream_id;
#ifdef DEBUG
UHashMap<UString> dtable;
HpackDynamicTable ddyntbl;
#endif
Connection();
~Connection()
{
U_TRACE_DTOR(0, Connection)
}
void reset()
{
U_TRACE_NO_PARAM(0, "UHTTP2::Connection::reset()")
inp_window =
out_window = HTTP2_DEFAULT_WINDOW_SIZE;
peer_settings = settings;
max_processed_stream_id = 0;
}
// SERVICES
static void preallocate(uint32_t max_connection)
{
U_TRACE(0+256, "UHTTP2::Connection::preallocate(%u)", max_connection)
U_INTERNAL_ASSERT_MAJOR(max_connection, 0)
U_INTERNAL_ASSERT_EQUALS(vConnection, U_NULLPTR)
U_INTERNAL_DUMP("sizeof(Connection) = %u sizeof(Stream) = %u", sizeof(Connection), sizeof(Stream))
vConnection = new Connection[max_connection];
}
// DEBUG
#if defined(U_STDCPP_ENABLE) && defined(DEBUG)
const char* dump(bool reset) const U_EXPORT;
#endif
private:
U_DISALLOW_COPY_AND_ASSIGN(Connection)
};
#ifdef DEBUG
static void testHpackDynTbl();
#endif
protected:
enum FrameTypesId {
DATA = 0x00,
HEADERS = 0x01,
PRIORITY = 0x02,
RST_STREAM = 0x03,
SETTINGS = 0x04,
PUSH_PROMISE = 0x05,
PING = 0x06,
GOAWAY = 0x07,
WINDOW_UPDATE = 0x08,
CONTINUATION = 0x09
};
enum FrameFlagsId {
FLAG_NONE = 0x00,
FLAG_ACK = 0x01, // 1
FLAG_END_STREAM = 0x01, // 1
FLAG_END_HEADERS = 0x04, // 100
FLAG_PADDED = 0x08, // 1000
FLAG_PRIORITY = 0x20 // 100000
};
enum FrameErrorCode { // The status codes for the RST_STREAM and GOAWAY frames
NO_ERROR = 0x00,
PROTOCOL_ERROR = 0x01,
INTERNAL_ERROR = 0x02,
FLOW_CONTROL_ERROR = 0x03,
SETTINGS_TIMEOUT = 0x04,
STREAM_CLOSED = 0x05,
FRAME_SIZE_ERROR = 0x06,
REFUSED_STREAM = 0x07,
CANCEL = 0x08,
COMPRESSION_ERROR = 0x09,
CONNECT_ERROR = 0x0a,
ENHANCE_YOUR_CALM = 0x0b,
INADEQUATE_SECURITY = 0x0c,
HTTP_1_1_REQUIRED = 0x0d,
ERROR_INCOMPLETE = 0x0e
};
enum HuffDecodeFlag {
HUFF_ACCEPTED = 1, // FSA accepts this state as the end of huffman encoding sequence
HUFF_SYM = (1 << 1), // This state emits symbol
HUFF_FAIL = (1 << 2) // If state machine reaches this state, decoding fails
};
/**
* All frames begin with a fixed 9-octet header followed by a variable-length payload
*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Length (24) |
* +---------------+---------------+---------------+
* | Type (8) | Flags (8) |
* +-+-+-----------+---------------+-------------------------------+
* |R| Stream Identifier (31) |
* +=+=============================================================+
* | Frame Payload (0...) ...
* +---------------------------------------------------------------+
*/
struct FrameHeader {
unsigned char* payload;
uint32_t length, // The length field of this frame, excluding frame header
stream_id; // The stream identifier (aka, stream ID)
uint8_t type, // The type of this frame
flags;
};
struct HuffSym {
uint32_t nbits, // The number of bits in this code
code; // Huffman code aligned to LSB
};
/**
* huffman decoding state, which is actually the node ID of internal huffman tree.
* We have 257 leaf nodes, but they are identical to root node other than emitting
* a symbol, so we have 256 internal nodes [1..255], inclusive
*/
struct HuffDecode {
uint8_t state,
flags, // bitwise OR of zero or more of the HuffDecodeFlag
sym; // symbol if HUFF_SYM flag set
};
static Stream* pStream;
static FrameHeader frame;
static Stream* pStreamEnd;
static int nerror, hpack_errno;
static Connection* vConnection;
static Connection* pConnection;
static const Settings settings;
static UString* http2_bug_client_txt;
static uint32_t wait_for_continuation;
static bool bcontinue100, bsetting_ack, bsetting_send;
static uint8_t priority_weight; // 0 if not set
static bool priority_exclusive;
static uint32_t priority_dependency; // 0 if not set
static const HuffSym huff_sym_table[];
static const HuffDecode huff_decode_table[][16];
static uint32_t hash_static_table[61];
static HpackHeaderTableEntry hpack_static_table[61];
// SERVICES
static void ctor();
static void dtor()
{
U_TRACE_NO_PARAM(0, "UHTTP2::dtor()")
delete[] vConnection;
if (http2_bug_client_txt) delete http2_bug_client_txt;
}
static void sendPing();
static void readFrame();
static void setStream();
static void manageData();
static bool initRequest();
static void writeResponse();
static void decodeHeaders();
static void manageHeaders();
static void handlerRequest();
static void handlerResponse();
static void sendResetStream();
static void sendWindowUpdate();
static void sendGoAway(USocket* psocket);
static void updateSetting(unsigned char* ptr, uint32_t len);
static void writeData(struct iovec* iov, bool bdata, bool flag);
static void handlerDelete(UClientImage_Base* pclient, bool& bsocket_open);
static unsigned char* setHpackHeaders(unsigned char* dst, const UString& headers);
static void startRequest()
{
U_TRACE_NO_PARAM(0, "UHTTP2::startRequest()")
UClientImage_Base::endRequest();
UHTTP::startRequest();
U_http_version = '2';
UClientImage_Base::resetBuffer();
}
static void resetDataRead()
{
U_TRACE_NO_PARAM(0, "UHTTP2::resetDataRead()")
UClientImage_Base::rstart = 0;
UClientImage_Base::rbuffer->setEmptyForce();
}
static void clear()
{
U_TRACE_NO_PARAM(0+256, "UHTTP2::clear()")
for (pStream = pConnection->streams; pStream <= pStreamEnd; ++pStream)
{
pStream->body.clear();
pStream->headers.clear();
pStream->id =
pStream->state =
pStream->clength = 0;
}
# ifdef DEBUG
for (pStreamEnd = (pConnection->streams+HTTP2_MAX_CONCURRENT_STREAMS); pStream < pStreamEnd; ++pStream)
{
// U_INTERNAL_DUMP("pStream index = %u", pStream - pConnection->streams)
U_ASSERT(pStream->body.empty())
U_ASSERT(pStream->headers.empty())
U_INTERNAL_ASSERT_EQUALS(pStream->clength, 0)
}
# endif
}
static void readPriority(unsigned char* ptr)
{
U_TRACE(0, "UHTTP2::readPriority(%p)", ptr)
priority_weight = ptr[4]+1;
uint32_t u4 = u_parse_unalignedp32(ptr);
priority_exclusive = u4 >> 31;
priority_dependency = u4 & 0x7fffffff;
U_INTERNAL_DUMP("priority_weight = %u priority_exclusive = %b priority_dependency = %u frame.stream_id = %u",
priority_weight, priority_exclusive, priority_dependency, frame.stream_id)
if (priority_dependency == frame.stream_id) nerror = PROTOCOL_ERROR;
}
static void setEncoding(const UString& x)
{
U_TRACE(0, "UHTTP2::setEncoding(%V)", x.rep)
U_INTERNAL_ASSERT_EQUALS(U_http_is_accept_gzip, false)
U_INTERNAL_DUMP("Accept-Encoding: = %V", x.rep)
U_INTERNAL_ASSERT(u_find(x.data(), 30, U_CONSTANT_TO_PARAM("gzip")))
U_http_flag |= HTTP_IS_ACCEPT_GZIP;
U_INTERNAL_DUMP("U_http_is_accept_gzip = %b", U_http_is_accept_gzip)
}
static void setURI(const char* ptr, uint32_t len)
{
U_TRACE(0, "UHTTP2::setURI(%.*S,%u)", len, ptr, len)
U_INTERNAL_ASSERT_EQUALS(U_http_info.uri_len, 0)
U_http_info.query = (const char*) memchr((U_http_info.uri = ptr), '?', (U_http_info.uri_len = len));
U_INTERNAL_DUMP("URI = %.*S", U_HTTP_URI_TO_TRACE)
if (U_http_info.query)
{
U_http_info.uri_len = U_http_info.query++ - ptr;
U_http_info.query_len = len - U_http_info.uri_len - 1;
U_INTERNAL_DUMP("query = %.*S", U_HTTP_QUERY_TO_TRACE)
}
}
static void setURI(const UString& uri) { setURI(U_STRING_TO_PARAM(uri)); }
static void updateSetting(const UString& data) { updateSetting((unsigned char*)U_STRING_TO_PARAM(data)); }
static bool writev(struct iovec* iov, int iovcnt, uint32_t count)
{
U_TRACE(0, "UHTTP2::writev(%p,%d,%u)", iov, iovcnt, count)
if (USocketExt::writev(UServer_Base::csocket, iov, iovcnt, count, 0) == count) U_RETURN(true);
nerror = CONNECT_ERROR;
U_RETURN(false);
}
static void checkStreamState()
{
U_TRACE_NO_PARAM(0, "UHTTP2::checkStreamState()")
if (pStream->state < STREAM_STATE_HALF_CLOSED)
{
if ((frame.flags & FLAG_END_STREAM) != 0) pStream->state = STREAM_STATE_HALF_CLOSED;
else
{
if (frame.type == HEADERS) nerror = PROTOCOL_ERROR;
}
}
else
{
if (frame.type != RST_STREAM &&
frame.type != WINDOW_UPDATE)
{
nerror = STREAM_CLOSED;
}
}
}
/**
* +-------+-----------------------------+---------------+
* | 1 | :authority | |
* | 2 | :method | GET |
* | 3 | :method | POST |
* | 4 | :path | / |
* | 5 | :path | /index.html |
* | 6 | :scheme | http |
* | 7 | :scheme | https |
* | 8 | :status | 200 |
* | 9 | :status | 204 |
* | 10 | :status | 206 |
* | 11 | :status | 304 |
* | 12 | :status | 400 |
* | 13 | :status | 404 |
* | 14 | :status | 500 |
* | ... | ... | ... |
* +-------+-----------------------------+---------------+
*/
static bool isHeaderToErase(UStringRep* key)
{
U_TRACE(0, "UHTTP2::isHeaderToErase(%V)", key)
if (key == UString::str_path->rep ||
key == UString::str_method->rep ||
key == UString::str_authority->rep ||
key == UString::str_user_agent->rep ||
key == UString::str_accept_encoding->rep)
{
U_RETURN(false);
}
U_RETURN(true);
}
static bool isHeaderToCopy(UStringRep* key)
{
U_TRACE(0, "UHTTP2::isHeaderToCopy(%V)", key)
if (isHeaderToErase(key) == false &&
key != UString::str_cookie->rep &&
key != UString::str_accept->rep &&
key != UString::str_referer->rep &&
key != UString::str_content_type->rep &&
key != UString::str_content_length->rep &&
key != UString::str_accept_language->rep)
{
U_RETURN(true);
}
U_RETURN(false);
}
static bool eraseHeaders(UStringRep* key, void* elem) // callWithDeleteForAllEntry()...
{
U_TRACE(0, "UHTTP2::eraseHeaders(%V,%p)", key, elem)
if (isHeaderToErase(key)) U_RETURN(true);
U_RETURN(false);
}
static bool copyHeaders(UStringRep* key, void* elem)
{
U_TRACE(0, "UHTTP2::copyHeaders(%V,%p)", key, elem)
if (isHeaderToCopy(key)) UClientImage_Base::request->snprintf_add(U_CONSTANT_TO_PARAM("%v: %v\r\n"), key, (const UStringRep*)elem);
U_RETURN(true);
}
static void downgradeRequest(); // HTTP2 => HTTP1
#ifdef USE_LOAD_BALANCE
static bool bproxy;
static void wrapRequest();
#endif
static bool setIndexStaticTable(UHashMap<void*>* table)
{
U_TRACE(0, "UHTTP2::setIndexStaticTable(%p)", table)
// if (bhash) UHashMap<void*>::lhash = u_hash_ignore_case((unsigned char*)U_STRING_TO_PARAM(*UHashMap<void*>::lkey));
UHashMap<void*>::setIdx(table);
U_RETURN(true); // NB: ignore case...
}
// HPACK (HTTP headers compression)
static bool isHpackError()
{
U_TRACE_NO_PARAM(0, "UHTTP2::isHpackError()")
U_INTERNAL_DUMP("hpack_errno = %d", hpack_errno)
if (hpack_errno)
{
nerror = COMPRESSION_ERROR;
U_RETURN(true);
}
U_RETURN(false);
}
static bool isHpackError(int32_t index)
{
U_TRACE(0, "UHTTP2::isHpackError(%d)", index)
U_INTERNAL_DUMP("index = %d hpack_errno = %d", index, hpack_errno)
if (index == -1 ||
hpack_errno)
{
# ifdef DEBUG
if (hpack_errno == 0) hpack_errno = -2; // The decoding buffer ends before the decoded HPACK block
# endif
nerror = COMPRESSION_ERROR;
U_RETURN(true);
}
U_RETURN(false);
}
static void setHpackDynTblCapacity(HpackDynamicTable* dyntbl, uint32_t value)
{
U_TRACE(0, "UHTTP2::setHpackDynTblCapacity(%p,%u)", dyntbl, value)
U_INTERNAL_DUMP("hpack_capacity = %u hpack_max_capacity = %u", dyntbl->hpack_capacity, dyntbl->hpack_max_capacity)
U_INTERNAL_ASSERT_DIFFERS(dyntbl->hpack_capacity, value)
dyntbl->hpack_capacity = value;
// adjust the size
while (dyntbl->num_entries > 0 &&
dyntbl->hpack_size > dyntbl->hpack_capacity)
{
evictHpackDynTblFirstEntry(dyntbl);
}
}
static void setHpackInputDynTblCapacity(uint32_t value)
{
U_TRACE(0, "UHTTP2::setHpackInputDynTblCapacity(%u)", value)
HpackDynamicTable* dyntbl = &(pConnection->idyntbl);
U_INTERNAL_DUMP("hpack_capacity = %u hpack_max_capacity = %u", dyntbl->hpack_capacity, dyntbl->hpack_max_capacity)
if (value != dyntbl->hpack_capacity)
{
dyntbl->hpack_max_capacity = value;
setHpackDynTblCapacity(dyntbl, value);
}
}
static unsigned char* setHpackOutputDynTblCapacity(unsigned char* dst, uint32_t& value)
{
U_TRACE(0, "UHTTP2::setHpackOutputDynTblCapacity(%p,%u)", dst, value)
HpackDynamicTable* dyntbl = &(pConnection->odyntbl);
U_INTERNAL_DUMP("hpack_capacity = %u hpack_max_capacity = %u", dyntbl->hpack_capacity, dyntbl->hpack_max_capacity)
if (value > 4096) value = 4096;
if (value != dyntbl->hpack_capacity)
{
setHpackDynTblCapacity(dyntbl, value);
dst = hpackEncodeInt(dst, value, (1<<5)-1, 0x20);
}
U_RETURN_POINTER(dst, unsigned char);
}
static HpackHeaderTableEntry* getHpackDynTblEntry(HpackDynamicTable* dyntbl, uint32_t index)
{
U_TRACE(0, "UHTTP2::getHpackDynTblEntry(%p,%u)", dyntbl, index)
U_INTERNAL_DUMP("dyntbl->entry_start_index = %u dyntbl->entry_capacity = %u dyntbl->num_entries = %u", dyntbl->entry_start_index, dyntbl->entry_capacity, dyntbl->num_entries)
U_INTERNAL_ASSERT_MAJOR(dyntbl->num_entries, 0)
U_INTERNAL_ASSERT_MINOR(index, dyntbl->num_entries)
HpackHeaderTableEntry* entry = dyntbl->entries + ((dyntbl->entry_start_index+index) % dyntbl->entry_capacity);
U_INTERNAL_ASSERT_POINTER(entry->name)
U_INTERNAL_ASSERT_POINTER(entry->value)
U_INTERNAL_DUMP("entry->name = %V entry->value = %V", entry->name, entry->value)
U_RETURN_POINTER(entry, HpackHeaderTableEntry);
}
static void evictHpackDynTblEntry(HpackDynamicTable* dyntbl, HpackHeaderTableEntry* entry)
{
U_TRACE(0, "UHTTP2::evictHpackDynTblEntry(%p,%p)", dyntbl, entry)
dyntbl->num_entries--;
dyntbl->hpack_size -= entry->name->size() + entry->value->size() + HTTP2_HEADER_TABLE_ENTRY_SIZE_OFFSET;
U_INTERNAL_ASSERT_POINTER(entry->name)
U_INTERNAL_ASSERT_POINTER(entry->value)
// NB: we decreases the reference string...
U_INTERNAL_DUMP("entry->name = %V entry->value = %V", entry->name, entry->value)
entry->name->release();
entry->value->release();
entry->name =
entry->value = U_NULLPTR;
}
static void evictHpackDynTblFirstEntry(HpackDynamicTable* dyntbl)
{
U_TRACE(0, "UHTTP2::evictHpackDynTblFirstEntry(%p)", dyntbl)
evictHpackDynTblEntry(dyntbl, getHpackDynTblEntry(dyntbl, dyntbl->num_entries-1));
}
static void evictHpackDynTblLastEntry(HpackDynamicTable* dyntbl)
{
U_TRACE(0, "UHTTP2::evictHpackDynTblLastEntry(%p)", dyntbl)
evictHpackDynTblEntry(dyntbl, getHpackDynTblEntry(dyntbl, 0));
dyntbl->entry_start_index = (dyntbl->entry_start_index+1+dyntbl->entry_capacity) % dyntbl->entry_capacity;
}
static void clearHpackDynTbl( HpackDynamicTable* dyntbl);
static void addHpackDynTblEntry(HpackDynamicTable* dyntbl, const UString& name, const UString& value);
static void evictHpackDynTblEntry(HpackDynamicTable* dyntbl, HpackHeaderTableEntry* entry, uint32_t index);
static void addHpackOutputDynTblEntry(const UString& name, const UString& value) { addHpackDynTblEntry(&(pConnection->odyntbl), name, value); }
static unsigned char* hpackDecodeInt( unsigned char* src, unsigned char* src_end, int32_t& value, uint8_t prefix_max);
static unsigned char* hpackDecodeString(unsigned char* src, unsigned char* src_end, UString& value);
static unsigned char* hpackEncodeInt(unsigned char* dst, uint32_t value, uint8_t prefix_max, uint8_t pattern)
{
U_TRACE(0+256, "UHTTP2::hpackEncodeInt(%p,%u,%u,%u)", dst, value, prefix_max, pattern)
/*
# ifdef DEBUG
unsigned char* src = dst;
uint32_t value_save = value;
# endif
*/
if (value < prefix_max) *dst++ = (uint8_t)(value | pattern);
else
{
*dst++ = (pattern | prefix_max);
for (value -= prefix_max; value >= 0x80; value >>= 7) *dst++ = 0x80 | (value & 0x7f);
*dst++ = (uint8_t)value;
}
/*
# ifdef DEBUG
int32_t index;
(void) hpackDecodeInt(src, src+(dst-src), index, prefix_max);
U_INTERNAL_ASSERT_EQUALS((uint32_t)index, value_save)
# endif
*/
U_RETURN_POINTER(dst, unsigned char);
}
static unsigned char* setHpackEncodeStringLen(unsigned char* dst, uint32_t sz)
{
U_TRACE(0, "UHTTP2::setHpackEncodeStringLen(%p,%u)", dst, sz)
uint32_t l = sz >> 3;
if (sz & 0x07) ++l;
dst = hpackEncodeInt(dst, l, (1<<7)-1, 0x80);
U_RETURN_POINTER(dst, unsigned char);
}
// Header field grammar validation (RFC 7230 Section 3.2)
static bool isHeaderName( const char* s, uint32_t n) __pure;
static bool isHeaderValue(const char* s, uint32_t n) __pure;
static bool isHeaderName( const UString& s) { return isHeaderName( U_STRING_TO_PARAM(s)); }
static bool isHeaderValue(const UString& s) { return isHeaderValue(U_STRING_TO_PARAM(s)); }
static const char* getFrameErrorCodeDescription(uint32_t error);
static unsigned char* hpackEncodeHeader(unsigned char* dst, const UString& key, const UString& value);
static void decodeHeaders(UHashMap<UString>* itable, HpackDynamicTable* dyntbl, unsigned char* ptr, unsigned char* endptr);
static unsigned char* hpackEncodeString(unsigned char* dst, const char* src, uint32_t len, bool bhuffman);
static unsigned char* hpackEncodeString(unsigned char* dst, const UString& value, bool bhuffman) { return hpackEncodeString(dst, U_STRING_TO_PARAM(value), bhuffman); }
#ifdef DEBUG
typedef struct { const char* str; int value; const char* desc; } HpackError;
static unsigned char* index_ptr;
static HpackError hpack_error[8];
static bool btest, bdecodeHeadersDebug, bhash;
static const char* hpack_strerror()
{
U_TRACE_NO_PARAM(0, "UHTTP2::hpack_strerror()")
U_INTERNAL_DUMP("hpack_errno = %d hpack_error[%d] = %S", hpack_errno, -hpack_errno-2, hpack_error[-hpack_errno-2].desc)
return hpack_error[-hpack_errno-2].desc;
}
static bool findHeader(uint32_t index)
{
U_TRACE(0, "UHTTP2::findHeader(%u)", index)
UHashMap<void*>::lhash = hash_static_table[index];
if (pConnection->dtable.lookup(hpack_static_table[index].name)) U_RETURN(true);
U_RETURN(false);
}
static const char* getStreamStatusDescription();
static const char* getConnectionStatusDescription();
static const char* getFrameTypeDescription(char type);
static void printHpackDynamicTable(HpackDynamicTable* dyntbl, ostream& os);
static void printHpackInputDynTable() { printHpackDynamicTable(&(pConnection->idyntbl), cout); }
static void printHpackOutputDynTable() { printHpackDynamicTable(&(pConnection->odyntbl), cout); }
# ifdef U_STDCPP_ENABLE
friend ostream& operator<<(ostream& os, HpackDynamicTable& v)
{
U_TRACE(0+256, "HpackDynamicTable::operator<<(%p,%p)", &os, &v)
printHpackDynamicTable(&v, os);
return os;
}
# endif
/**
* static void eraseHeader(uint32_t index)
* {
* U_TRACE(0, "UHTTP2::eraseHeader(%u)", index)
*
* UHashMap<void*>::lhash = hash_static_table[index];
*
* UHashMap<UString>* table = &(pConnection->itable);
*
* if (table->lookup(hpack_static_table[index].name)) table->eraseAfterFind();
* }
*
* static void decodeHeadersResponse(unsigned char* ptr, uint32_t length)
* {
* U_TRACE(0, "UHTTP2::decodeHeadersResponse(%p,%u)", ptr, length)
*
* bdecodeHeadersDebug = true;
*
* decodeHeaders(&(pConnection->dtable),
* &(pConnection->ddyntbl), ptr, ptr+length));
*
* bdecodeHeadersDebug = false;
*
* U_DUMP_OBJECT_TO_TMP(pConnection->dtable, response.dtable)
* U_DUMP_OBJECT_TO_TMP(pConnection->ddyntbl, response.ddyntbl)
*
* U_ASSERT(findHeader(53)) // UString::str_server
* U_ASSERT(findHeader(32)) // UString::str_date
* U_ASSERT(findHeader(27)) // UString::str_content_length
*
* pConnection->dtable.clear();
* clearHpackDynTbl(&(pConnection->ddyntbl));
* }
*
* We save the Header Block Fragment of the frame to inspect it with inflatehd (https://github.com/tatsuhiro-t/nghttp2)
*
* ./inflatehd < inflatehd.json => { "cases": [ { "wire": "8285" } ] }
*
* static void saveHpackData(const char* ptr, uint32_t len, bool breq)
* {
* U_TRACE(0+256, "UHTTP2::saveHpackData(%.*S,%u,%b)", len, ptr, len, breq)
*
* UString tmp(U_CAPACITY);
*
* UHexDump::encode(ptr, len, tmp);
* }
*/
#endif
private:
U_DISALLOW_COPY_AND_ASSIGN(UHTTP2)
friend class UHTTP;
friend class Application;
friend class UClientImage_Base;
};
#endif