mirror of
https://github.com/stefanocasazza/ULib.git
synced 2025-09-28 19:05:55 +08:00
324 lines
9.9 KiB
C++
324 lines
9.9 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/container/hash_map.h>
|
|
|
|
#define HTTP2_CONNECTION_PREFACE "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" // (24 bytes)
|
|
|
|
class UHTTP;
|
|
class UClientImage_Base;
|
|
class UHashMap<UString>;
|
|
|
|
class U_EXPORT UHTTP2 {
|
|
public:
|
|
|
|
static void ctor();
|
|
static void dtor();
|
|
|
|
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,
|
|
FLAG_END_STREAM = 0x01,
|
|
FLAG_END_HEADERS = 0x04,
|
|
FLAG_PADDED = 0x08,
|
|
FLAG_PRIORITY = 0x20
|
|
};
|
|
|
|
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 = 0xff
|
|
};
|
|
|
|
/**
|
|
* 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;
|
|
int32_t length; // The length field of this frame, excluding frame header
|
|
int32_t stream_id; // The stream identifier (aka, stream ID)
|
|
uint8_t type; // The type of this frame
|
|
uint8_t flags;
|
|
};
|
|
|
|
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;
|
|
uint32_t enable_push;
|
|
uint32_t max_concurrent_streams;
|
|
uint32_t initial_window_size;
|
|
uint32_t max_frame_size;
|
|
uint32_t max_header_list_size;
|
|
};
|
|
|
|
enum StreamState {
|
|
STREAM_STATE_IDLE = 0x000,
|
|
STREAM_STATE_RESERVED = 0x001,
|
|
STREAM_STATE_OPEN = 0x002,
|
|
STREAM_STATE_HALF_CLOSED = 0x004,
|
|
STREAM_STATE_CLOSED = 0x008
|
|
};
|
|
|
|
enum ConnectionState {
|
|
CONN_STATE_OPEN,
|
|
CONN_STATE_IS_CLOSING
|
|
};
|
|
|
|
struct Stream {
|
|
int id;
|
|
int state;
|
|
int input_window;
|
|
int output_window;
|
|
// priority
|
|
uint32_t priority_dependency; // 0 if not set
|
|
char priority_weight; // 0 if not set
|
|
bool priority_exclusive;
|
|
};
|
|
|
|
struct Connection {
|
|
ConnectionState state; // state
|
|
Settings peer_settings; // settings
|
|
UHashMap<UString> itable; // headers request
|
|
// internal
|
|
uint32_t input_window;
|
|
uint32_t output_window;
|
|
int32_t hpack_max_capacity; // the value set by SETTINGS_HEADER_TABLE_SIZE
|
|
// streams
|
|
int max_open_stream_id;
|
|
uint32_t num_responding_streams;
|
|
uint32_t max_processed_stream_id;
|
|
Stream streams[100];
|
|
};
|
|
|
|
struct HpackHeaderTableEntry {
|
|
UString* name;
|
|
UString* value;
|
|
};
|
|
|
|
static UString* str_authority;
|
|
static UString* str_method;
|
|
static UString* str_method_get;
|
|
static UString* str_method_post;
|
|
static UString* str_path;
|
|
static UString* str_path_root;
|
|
static UString* str_path_index;
|
|
static UString* str_scheme;
|
|
static UString* str_scheme_http;
|
|
static UString* str_scheme_https;
|
|
static UString* str_status;
|
|
static UString* str_status_200;
|
|
static UString* str_status_204;
|
|
static UString* str_status_206;
|
|
static UString* str_status_304;
|
|
static UString* str_status_400;
|
|
static UString* str_status_404;
|
|
static UString* str_status_500;
|
|
static UString* str_accept_charset;
|
|
static UString* str_accept_encoding;
|
|
static UString* str_accept_encoding_value;
|
|
static UString* str_accept_language;
|
|
static UString* str_accept_ranges;
|
|
static UString* str_accept;
|
|
static UString* str_access_control_allow_origin;
|
|
static UString* str_age;
|
|
static UString* str_allow;
|
|
static UString* str_authorization;
|
|
static UString* str_cache_control;
|
|
static UString* str_content_disposition;
|
|
static UString* str_content_encoding;
|
|
static UString* str_content_language;
|
|
static UString* str_content_length;
|
|
static UString* str_content_location;
|
|
static UString* str_content_range;
|
|
static UString* str_content_type;
|
|
static UString* str_cookie;
|
|
static UString* str_date;
|
|
static UString* str_etag;
|
|
static UString* str_expect;
|
|
static UString* str_expires;
|
|
static UString* str_from;
|
|
static UString* str_host;
|
|
static UString* str_if_match;
|
|
static UString* str_if_modified_since;
|
|
static UString* str_if_none_match;
|
|
static UString* str_if_range;
|
|
static UString* str_if_unmodified_since;
|
|
static UString* str_last_modified;
|
|
static UString* str_link;
|
|
static UString* str_location;
|
|
static UString* str_max_forwards;
|
|
static UString* str_proxy_authenticate;
|
|
static UString* str_proxy_authorization;
|
|
static UString* str_range;
|
|
static UString* str_referer;
|
|
static UString* str_refresh;
|
|
static UString* str_retry_after;
|
|
static UString* str_server;
|
|
static UString* str_set_cookie;
|
|
static UString* str_strict_transport_security;
|
|
static UString* str_transfer_encoding;
|
|
static UString* str_user_agent;
|
|
static UString* str_vary;
|
|
static UString* str_via;
|
|
static UString* str_www_authenticate;
|
|
|
|
static int nerror;
|
|
static Stream* pStream;
|
|
static bool settings_ack;
|
|
static FrameHeader frame;
|
|
static void* pConnectionEnd;
|
|
static Connection* pConnection;
|
|
static const Settings settings;
|
|
static const char* upgrade_settings;
|
|
|
|
static uint32_t hash_static_table[61];
|
|
static HpackHeaderTableEntry hpack_static_table[61];
|
|
|
|
// SERVICES
|
|
|
|
static void readFrame();
|
|
static void sendError();
|
|
static void manageData();
|
|
static void manageHeaders();
|
|
static int handlerRequest();
|
|
static void handlerResponse();
|
|
static bool readBodyRequest();
|
|
static bool updateSetting(unsigned char* ptr, uint32_t len);
|
|
static void decodeHeaders(unsigned char* ptr, unsigned char* endptr);
|
|
|
|
#ifdef DEBUG
|
|
static const char* getFrameTypeDescription(char type);
|
|
#endif
|
|
|
|
static bool setIndexStaticTable(UHashMap<void*>* ptable, const char* key, uint32_t length)
|
|
{
|
|
U_TRACE(0, "UHTTP2::setIndexStaticTable(%p,%.*S,%u)", ptable, length, key, length)
|
|
|
|
ptable->index = ptable->hash % ptable->_capacity;
|
|
|
|
U_RETURN(false);
|
|
}
|
|
|
|
static void setLengthAndType(char* ptr, uint32_t length, FrameTypesId type)
|
|
{
|
|
U_TRACE(0, "UHTTP2::setLengthAndType(%p,%u,%d)", ptr, length, type)
|
|
|
|
U_INTERNAL_ASSERT(type <= CONTINUATION)
|
|
|
|
*(uint32_t*)ptr = htonl(length & 0x00ffffff) >> 8;
|
|
ptr[3] = type;
|
|
|
|
U_INTERNAL_DUMP("length = %#.4S", ptr) // "\000\000\004\003" (big endian: 0x11223344)
|
|
}
|
|
|
|
// HPACK
|
|
|
|
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
|
|
};
|
|
|
|
struct HuffDecode {
|
|
/**
|
|
* 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
|
|
*/
|
|
uint8_t state;
|
|
uint8_t flags; // bitwise OR of zero or more of the HuffDecodeFlag
|
|
uint8_t sym; // symbol if HUFF_SYM flag set
|
|
};
|
|
|
|
struct HuffSym {
|
|
uint32_t nbits; // The number of bits in this code
|
|
uint32_t code; // Huffman code aligned to LSB
|
|
};
|
|
|
|
static const HuffSym huff_sym_table[];
|
|
static const HuffDecode huff_decode_table[][16];
|
|
|
|
static unsigned char* hpackEncodeString(unsigned char* dst, const char* src, uint32_t len);
|
|
static unsigned char* hpackDecodeString(unsigned char* src, unsigned char* src_end, UString* pvalue);
|
|
|
|
static unsigned char* hpackEncodeInt( unsigned char* dst, uint32_t value, uint8_t prefix_max);
|
|
static unsigned char* hpackDecodeInt( unsigned char* src, unsigned char* src_end, int32_t* pvalue, uint8_t prefix_max);
|
|
|
|
private:
|
|
static UVector<UString>* vext;
|
|
|
|
friend class UHTTP;
|
|
friend class UClientImage_Base;
|
|
|
|
#ifdef U_COMPILER_DELETE_MEMBERS
|
|
UHTTP2(const UHTTP2&) = delete;
|
|
UHTTP2& operator=(const UHTTP2&) = delete;
|
|
#else
|
|
UHTTP2(const UHTTP2&) {}
|
|
UHTTP2& operator=(const UHTTP2&) { return *this; }
|
|
#endif
|
|
};
|
|
|
|
#endif
|