diff --git a/sdk/base.hpp b/sdk/base.hpp index 9892cfed..91e9481e 100644 --- a/sdk/base.hpp +++ b/sdk/base.hpp @@ -30,8 +30,10 @@ #include #include #include +#include #include #include +#include #include #include diff --git a/sdk/base/basic/huffman_coding.cpp b/sdk/base/basic/huffman_coding.cpp new file mode 100644 index 00000000..31f6a93e --- /dev/null +++ b/sdk/base/basic/huffman_coding.cpp @@ -0,0 +1,439 @@ +/* vim: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab smarttab : */ +/** + * @file {file} + * @author Soo Han, Kim (princeb612.kr@gmail.com) + * @desc + * + * Revision History + * Date Name Description + * 2023.09.12 Soo Han, Kim CBOR (codename.hotplace) + */ + +#include + +namespace hotplace { + +huffman_coding::huffman_coding() {} + +huffman_coding::~huffman_coding() { + _measure.clear(); + _btree.clear(); + _m.clear(); + _codetable.clear(); + _reverse_codetable.clear(); +} + +void huffman_coding::reset() { _measure.clear(); } + +huffman_coding &huffman_coding::operator<<(const char *s) { return load(s); } + +huffman_coding &huffman_coding::load(const char *s) { + // count + if (s) { + for (const char *p = s; *p; p++) { + _measure.insert(hc_t((uint8)*p), [](hc_t &t) -> void { t.weight++; }); + } + } + return *this; +} + +huffman_coding &huffman_coding::learn() { + _btree.clear(); + _m.clear(); + _codetable.clear(); + _reverse_codetable.clear(); + + _measure.for_each([&](hc_t const &t) -> void { _btree.insert(t); }); + + while (_btree.size() > 1) { + hc_t k; + hc_t k_lhs; + hc_t k_rhs; + + typename btree_t::node_t *l = _btree.clone_nocascade(_btree.first()); + k_lhs = l->_key; + _btree.remove(l->_key); + + typename btree_t::node_t *r = _btree.clone_nocascade(_btree.first()); + k_rhs = r->_key; + _btree.remove(r->_key); + + k.symbol = k_lhs.symbol; + k.weight = k_lhs.weight + k_rhs.weight; + k.flags = 1; // merged + + typename btree_t::node_t *newone = _btree.insert(k, _btree._root); // merged + + map_pib_t pib = _m.insert(std::make_pair(k, _btree.clone_nocascade(newone))); + pib.first->second->_left = l; + pib.first->second->_right = r; + } + return *this; +} + +huffman_coding &huffman_coding::infer() { + huffman_coding::node_t *root = nullptr; + build(&root); + + if (root) { + hc_temp hc; + infer(hc, root); + + clear(root); + } + + return *this; +} + +huffman_coding &huffman_coding::imports(const hc_code_t *table) { + _codetable.clear(); + _reverse_codetable.clear(); + + for (size_t i = 0; table; i++) { + const hc_code_t *item = table + i; + if (nullptr == item->code) { + break; + } + _codetable.insert(std::make_pair(item->sym, item->code)); + _reverse_codetable.insert(std::make_pair(item->code, item->sym)); + } + + return *this; +} + +huffman_coding &huffman_coding::exports(std::function v) { + for (auto item : _codetable) { + v(item.first, item.second.c_str()); + } + return *this; +} + +return_t huffman_coding::expect(const char *source, size_t &size_expected) { + return_t ret = errorcode_t::success; + __try2 { + size_expected = 0; + + if (nullptr == source) { + ret = errorcode_t::invalid_parameter; + __leave2; + } + ret = expect((byte_t *)source, strlen(source), size_expected); + } + __finally2 { + // do nothing + } + return ret; +} + +return_t huffman_coding::expect(const char *source, size_t size, size_t &size_expected) { + return_t ret = errorcode_t::success; + __try2 { + size_expected = 0; + + if (nullptr == source) { + ret = errorcode_t::invalid_parameter; + __leave2; + } + ret = expect((byte_t *)source, size, size_expected); + } + __finally2 { + // do nothing + } + return ret; +} + +return_t huffman_coding::expect(byte_t *source, size_t size, size_t &size_expected) { + return_t ret = errorcode_t::success; + __try2 { + size_expected = 0; + size_t sum = 0; + + if (nullptr == source) { + ret = errorcode_t::invalid_parameter; + __leave2; + } + if (_codetable.empty()) { + ret = errorcode_t::not_ready; + __leave2; + } + + maphint hint(_codetable); + size_t i = 0; + byte_t *p = source; + for (i = 0; i < size; i++) { + std::string code; + hint.find(p[i], &code); + sum += code.size(); + } + // align (2^n) | (x + (align-1)) & ~(align-1) | output | + // 4 | (x + 3) & ~3 | 1..4 -> 4, 5..8 -> 8, ... | + // 8 | (x + 7) & ~7 | 1..8 -> 8, 9..16 -> 16, ... | + size_expected = ((sum + 7) & ~7) >> 3; + } + __finally2 { + // do nothing + } + return ret; +} + +return_t huffman_coding::encode(binary_t &bin, const char *source, size_t size, bool usepad) { return encode(bin, (byte_t *)source, size, usepad); } + +return_t huffman_coding::encode(binary_t &bin, byte_t *source, size_t size, bool usepad) { + return_t ret = errorcode_t::success; + std::string buf; + std::string code; + size_t totalbits = 0; + byte_t *p = nullptr; + size_t i = 0; + maphint hint(_codetable); + + // RFC 7541 Appendix B. Huffman Code + // As the Huffman-encoded data doesn't always end at an octet boundary, + // some padding is inserted after it, up to the next octet boundary. + + // RFC 7541 5.2. String Literal Representation + // As the Huffman-encoded data doesn't always end at an octet boundary, + // some padding is inserted after it, up to the next octet boundary. To + // prevent this padding from being misinterpreted as part of the string + // literal, the most significant bits of the code corresponding to the + // EOS (end-of-string) symbol are used. + + // if min(code len in bits) >= 5 ... no problem while encode/decode + // huffman_coding huff; + // huff.imports(_h2hcodes); + // // EOS 256 111111111111111111111111111111 3fffffff [30] + // else min(code len in bits) < 5 ... ambiguous + // // A |1010 a [4] + // // B |1111 f [4] + // // 1010 1111 -> AB + // // 1010 pppp -> AB or A ? + + __try2 { + if (nullptr == source) { + ret = errorcode_t::invalid_parameter; + __leave2; + } + if (_reverse_codetable.empty()) { + ret = errorcode_t::not_ready; + __leave2; + } + + size_t code_msize = _reverse_codetable.begin()->first.size(); + usepad &= (code_msize >= 5); // overwrite usepad + + // align to MSB + for (p = source, i = 0; i < size; i++) { + std::string code; + hint.find(p[i], &code); + buf += code; + + if (usepad) { + if (i == (size - 1)) { + size_t mod = (buf.size() % 8); + if (mod) { + size_t padsize = 8 - mod; + while (padsize--) { + buf += '1'; + } + } + } + } + + while (buf.size() >= 8) { + uint8 b = 0; + for (int n = 0; n < 8; n++) { + if ('1' == buf[n]) { + b |= (1 << (7 - n)); + } + } + bin.insert(bin.end(), b); + buf.erase(0, 8); + } + } + if (false == usepad) { + size_t remains = buf.size(); + if (remains) { + uint8 b = 0; + + for (int i = 0; i < remains; i++) { + if ('1' == buf[i]) { + b |= (1 << (7 - i)); + } + } + bin.insert(bin.end(), b); + buf.erase(0, remains); + } + } + } + __finally2 { + // do nothing + } + return ret; +} + +return_t huffman_coding::encode(stream_t *stream, const char *source, size_t size) { return encode(stream, (byte_t *)source, size); } + +return_t huffman_coding::encode(stream_t *stream, byte_t *source, size_t size) { + return_t ret = errorcode_t::success; + __try2 { + if (nullptr == stream || nullptr == source) { + ret = errorcode_t::invalid_parameter; + __leave2; + } + // align to MSB + byte_t *p = nullptr; + size_t i = 0; + maphint hint(_codetable); + for (p = source, i = 0; i < size; i++) { + std::string code; + hint.find(p[i], &code); + stream->printf("%s ", code.c_str()); + } + } + __finally2 { + // do nothing + } + return ret; +} + +return_t huffman_coding::decode(stream_t *stream, byte_t *source, size_t size) { + return_t ret = errorcode_t::success; + __try2 { + if ((nullptr == stream) || (nullptr == source)) { + ret = errorcode_t::invalid_parameter; + __leave2; + } + + if (_reverse_codetable.empty()) { + ret = errorcode_t::not_ready; + __leave2; + } + + size_t code_msize = _reverse_codetable.begin()->first.size(); + if (code_msize <= 4) { + // see encode + ret = errorcode_t::insufficient; + __leave2; + } + + std::string que; + std::string token; + + for (size_t i = 0; i < size; i++) { + byte_t b = source[i]; + for (int n = 7; n >= 0; n--) { + que += ((b & (1 << n)) ? '1' : '0'); + } + + while (que.size() >= code_msize) { + int count = 0; + for (size_t l = code_msize; l <= que.size(); l++) { + token = que.substr(0, l); + std::map::iterator iter = _reverse_codetable.find(token); + if (_reverse_codetable.end() != iter) { + stream->printf("%c", iter->second); + que.erase(0, l); + break; + } else { + count++; + } + } + if ((que.size() - code_msize + 1) == count) { + break; + } + } + } + + for (auto e : que) { + if ('1' != e) { + ret = errorcode_t::bad_data; + break; + } + } + } + __finally2 { + // do nothing + } + return ret; +} + +huffman_coding::node_t *huffman_coding::build(node_t **root) { + typename btree_t::node_t *p = nullptr; + if (_m.size()) { + p = _m.rbegin()->second; + _m.erase(p->_key); + + while (_m.size()) { + build(p); + } + + if (root) { + *root = p; + } + } + return p; +} + +void huffman_coding::clear(node_t *&root) { _btree.clear(root); } + +void huffman_coding::build(typename btree_t::node_t *&p) { + if (p) { + if (p->_left) { + build(p->_left); + } + if (p->_right) { + build(p->_right); + } + typename map_t::iterator iter = _m.find(p->_key); + if (_m.end() != iter) { + typename btree_t::node_t *t = iter->second; + + _btree.clear(p); + p = t; + _m.erase(iter); + } + } +} + +void huffman_coding::infer(hc_temp &hc, typename btree_t::node_t *t) { + if (t) { + hc.depth++; + + hc.code += "0"; + infer(hc, t->_left); + hc.code.pop_back(); + + if (0 == t->_key.flags) { + _codetable.insert(std::make_pair(t->_key.symbol, hc.code)); + _reverse_codetable.insert(std::make_pair(hc.code, t->_key.symbol)); + } + + hc.code += "1"; + infer(hc, t->_right); + hc.code.pop_back(); + + hc.depth--; + } +} + +bool huffman_coding::decodable() { + bool ret = false; + __try2 { + if (_reverse_codetable.empty()) { + __leave2; + } + + size_t code_msize = _reverse_codetable.begin()->first.size(); + if (code_msize <= 4) { + __leave2; + } + + ret = true; + } + __finally2 { + // do nothing + } + return ret; +} + +} // namespace hotplace diff --git a/sdk/base/basic/huffman_coding.hpp b/sdk/base/basic/huffman_coding.hpp index e56ea460..2d3f91d6 100644 --- a/sdk/base/basic/huffman_coding.hpp +++ b/sdk/base/basic/huffman_coding.hpp @@ -31,7 +31,10 @@ namespace hotplace { * https://asecuritysite.com/calculators/huff * @sample * huffman_coding huff; + * // method.1 - learn huffman codes from stream * huff.load(sample).learn().infer(); + * // method.2 - load from pre-trained codes + * huff.imports(_h2hcodes); */ template @@ -80,250 +83,116 @@ class huffman_coding { hc_temp() : depth(0) {} }; + struct hc_code { + uint8 sym; + const char *code; + }; + typedef t_btree measure_tree_t; typedef t_btree btree_t; typedef std::map map_t; typedef std::pair map_pib_t; typedef std::map codetable_t; - typedef typename btree_t::node_t node_t; + typedef std::map reverse_codetable_t; - public: + typedef typename btree_t::node_t node_t; typedef typename std::function const_visitor; typedef typename std::function visitor; typedef typename std::function learn_visitor; - huffman_coding() {} - ~huffman_coding() { - _measure.clear(); - _btree.clear(); - _m.clear(); - _codetable.clear(); - } - - void reset() { _measure.clear(); } - - huffman_coding &operator<<(const char *s) { return load(s); } - huffman_coding &load(const char *s) { - // count - if (s) { - for (const char *p = s; *p; p++) { - _measure.insert(hc_t((uint8)*p), [](hc_t &t) -> void { t.weight++; }); - } - } - return *this; - } - - huffman_coding &learn() { - _btree.clear(); - _m.clear(); - _codetable.clear(); - - _measure.for_each([&](hc_t const &t) -> void { _btree.insert(t); }); - - while (_btree.size() > 1) { - hc_t k; - hc_t k_lhs; - hc_t k_rhs; - - typename btree_t::node_t *l = _btree.clone_nocascade(_btree.first()); - k_lhs = l->_key; - _btree.remove(l->_key); - - typename btree_t::node_t *r = _btree.clone_nocascade(_btree.first()); - k_rhs = r->_key; - _btree.remove(r->_key); - - k.symbol = k_lhs.symbol; - k.weight = k_lhs.weight + k_rhs.weight; - k.flags = 1; // merged - - typename btree_t::node_t *newone = _btree.insert(k, _btree._root); // merged - - map_pib_t pib = _m.insert(std::make_pair(k, _btree.clone_nocascade(newone))); - pib.first->second->_left = l; - pib.first->second->_right = r; - } - return *this; - } - - huffman_coding &infer() { - huffman_coding::node_t *root = nullptr; - build(&root); - - if (root) { - hc_temp hc; - infer(hc, root); - - clear(root); - } - - return *this; - } - - void encode(binary_t &bin, byte_t *source, size_t size) { - return_t ret = errorcode_t::success; - std::string buf; - std::string code; - size_t totalbits = 0; - byte_t *p = nullptr; - size_t i = 0; - maphint hint(_codetable); - -#if 0 - // align to LSB - - for (p = source, i = 0; i < size; i++) { - ret = hint.find(p[i], &code); - if (errorcode_t::success != ret) { - ret = errorcode_t::bad_data; - break; - } - totalbits += code.size(); - } - - if (errorcode_t::success == ret) { - size_t remains = totalbits % 8; - - for (p = source, i = 0; i < size; i++) { - hint.find(p[i], &code); - - buf += code; - - if (remains) { - if (buf.size() > remains) { - uint8 b = 0; - for (size_t i = 0; i < remains; i++) { - if ('1' == buf[i]) { - b += (1 << (7 - i - remains)); - } - } - bin.insert(bin.end(), b); - buf.erase(0, remains); - - remains = 0; - } - } - - while (buf.size() >= 8) { - uint8 b = 0; - for (size_t i = 0; i < 8; i++) { - if ('1' == buf[i]) { - b += (1 << (7 - i)); - } - } - bin.insert(bin.end(), b); - buf.erase(0, 8); - } - } - } -#else - // align to MSB - for (p = source, i = 0; i < size; i++) { - std::string code; - hint.find(p[i], &code); - buf += code; - - while (buf.size() >= 8) { - uint8 b = 0; - for (int i = 0; i < 8; i++) { - if ('1' == buf[i]) { - b += (1 << (7 - i)); - } - } - bin.insert(bin.end(), b); - buf.erase(0, 8); - } - } - { - uint8 b = 0; - size_t remains = buf.size(); - for (int i = 0; i < remains; i++) { - if ('1' == buf[i]) { - b += (1 << (7 - i)); - } - } - bin.insert(bin.end(), b); - buf.erase(0, remains); - } -#endif - } - void encode(stream_t *s, byte_t *source, size_t size) { - // align to MSB - byte_t *p = nullptr; - size_t i = 0; - maphint hint(_codetable); - for (p = source, i = 0; i < size; i++) { - std::string code; - hint.find(p[i], &code); - s->printf("%s ", code.c_str()); - } - } + public: + typedef hc_code hc_code_t; + + huffman_coding(); + ~huffman_coding(); + + void reset(); + + huffman_coding &operator<<(const char *s); + huffman_coding &load(const char *s); + huffman_coding &learn(); + huffman_coding &infer(); + + /** + * @brief import pre-trained codes + * @sample + * const huffman_coding::hc_code_t _h2hcodes[] = { + * { 1, "11111111111111111011000" }, + * { 2, "1111111111111111111111100010" }, + * // ... + * }; + * + * huffman_coding huff; + * huff.imports(_h2hcodes); + */ + huffman_coding &imports(const hc_code_t *table); + /** + * @brief export hufman codes + * @sample + * const huffman_coding::hc_code_t _h2hcodes[] = { + * { 1, "11111111111111111011000" }, + * { 2, "1111111111111111111111100010" }, + * // ... + * }; + * + * huffman_coding huff; + * huff.imports(_h2hcodes).exports( + * [](uint8 sym, const char* code) -> void { printf("sym %c (0x%02x) %s (%zi)\n", isprint(sym) ? sym : '?', sym, code, strlen(code)); }); + */ + huffman_coding &exports(std::function v); + + return_t expect(const char *source, size_t &size_expected); + return_t expect(const char *source, size_t size, size_t &size_expected); + return_t expect(byte_t *source, size_t size, size_t &size_expected); + + /* + * @brief encode + * @sample + * const char* sample = "www.example.com"; + * binary_t bin; + * huff.encode(bin, (byte_t*)sample, strlen(sample)); + * // f1 e3 c2 e5 f2 3a 6b a0 ab 90 f4 ff + */ + return_t encode(binary_t &bin, const char *source, size_t size, bool usepad = true); + return_t encode(binary_t &bin, byte_t *source, size_t size, bool usepad = true); + /* + * @brief encode + * @sample + * const char* sample = "www.example.com"; + * basic_stream bs; + * huff.encode(&bs, (byte_t*)sample, strlen(sample)); + * printf("%s\n", bs.c_str()); + * + */ + return_t encode(stream_t *stream, const char *source, size_t size); + return_t encode(stream_t *stream, byte_t *source, size_t size); + /** + * @brief decode + * @remarks constraints : min(code len in bits) >= 5 + * + * huff.imports(_h2hcodes); // RFC 7541 Appendix B. Huffman Code + * huff.encode(...); + * huff.decode(...); + */ + return_t decode(stream_t *stream, byte_t *source, size_t size); + + /** + * @brief check min(code len in bits) >= 5 + */ + bool decodable(); protected: - node_t *build(node_t **root = nullptr) { - typename btree_t::node_t *p = nullptr; - if (_m.size()) { - p = _m.rbegin()->second; - _m.erase(p->_key); - - while (_m.size()) { - build(p); - } - - if (root) { - *root = p; - } - } - return p; - } - void clear(node_t *&root) { _btree.clear(root); } - - void build(typename btree_t::node_t *&p) { - if (p) { - if (p->_left) { - build(p->_left); - } - if (p->_right) { - build(p->_right); - } - typename map_t::iterator iter = _m.find(p->_key); - if (_m.end() != iter) { - typename btree_t::node_t *t = iter->second; - - _btree.clear(p); - p = t; - _m.erase(iter); - } - } - } - void infer(hc_temp &hc, typename btree_t::node_t *t) { - if (t) { - hc.depth++; - - hc.code += "0"; - infer(hc, t->_left); - hc.code.pop_back(); - - bool use = false; - uint8 symbol = 0; - - if (0 == t->_key.flags) { - _codetable.insert(std::make_pair(t->_key.symbol, hc.code)); - } - - hc.code += "1"; - infer(hc, t->_right); - hc.code.pop_back(); - - hc.depth--; - } - } + node_t *build(node_t **root = nullptr); + void clear(node_t *&root); + void build(typename btree_t::node_t *&p); + void infer(hc_temp &hc, typename btree_t::node_t *t); private: measure_tree_t _measure; btree_t _btree; map_t _m; codetable_t _codetable; + reverse_codetable_t _reverse_codetable; }; } // namespace hotplace diff --git a/sdk/net/http/http2/hpack.cpp b/sdk/net/http/http2/hpack.cpp index 6763c8b8..4e2d0559 100644 --- a/sdk/net/http/http2/hpack.cpp +++ b/sdk/net/http/http2/hpack.cpp @@ -12,10 +12,449 @@ #include namespace hotplace { -using namespace io; namespace net { -// study +hpack::hpack() { + _hc.imports(_h2hcodes); // RFC 7541 Appendix B. Huffman Code +} + +hpack& hpack::encode_int(binary_t& target, uint8 mask, uint8 prefix, size_t value) { + if ((1 <= prefix) && (prefix <= 8)) { + // RFC 7541 5.1. Integer Representation + // RFC 7541 C.1. Integer Representation Examples + // RFC 7541 Figure 3: Integer Value Encoded after the Prefix (Shown for N = 5) + // + // 0 1 2 3 4 5 6 7 + // +---+---+---+---+---+---+---+---+ + // | ? | ? | ? | 1 1 1 1 1 | + // +---+---+---+-------------------+ + // | 1 | Value-(2^N-1) LSB | + // +---+---------------------------+ + // ... + // +---+---------------------------+ + // | 0 | Value-(2^N-1) MSB | + // +---+---------------------------+ + + uint8 n = (1 << prefix) - 1; +#if 0 + // safety mask + if (mask) { + uint8 temp = 0; + for (int t = 0; t < prefix; t++ { + temp |= (1 << t); + } + mask &= ~temp; + } +#endif + + uint8 i = 0; + if (value < n) { + target.insert(target.end(), value | mask); + } else { + target.insert(target.end(), n | mask); + value -= n; + // 128 (0x80) + // 1 value + // 1 value + // 0 value + while (value >= 0x80) { + i = (value % 0x80) | 0x80; + target.insert(target.end(), i); + value /= 0x80; + } + target.insert(target.end(), value); + } + } + return *this; +} + +hpack& hpack::encode_string(binary_t& target, uint32 flags, const char* value) { + __try2 { + if (nullptr == value) { + __leave2; + } + encode_string(target, flags, value, strlen(value)); + } + __finally2 { + // do nothing + } + return *this; +} + +hpack& hpack::encode_string(binary_t& target, uint32 flags, const char* value, size_t size) { + __try2 { + if (nullptr == value) { + __leave2; + } + + // RFC 7541 Figure 4: String Literal Representation + // + // 0 1 2 3 4 5 6 7 + // +---+---+---+---+---+---+---+---+ + // | H | String Length (7+) | + // +---+---------------------------+ + // | String Data (Length octets) | + // +-------------------------------+ + + if (hpack_huffman & flags) { + size_t size_expected = 0; + _hc.expect(value, size, size_expected); + encode_int(target, 0x80, 7, size_expected); + _hc.encode(target, value, size); + } else { + encode_int(target, 0x00, 7, size); + target.insert(target.end(), value, value + size); + } + } + __finally2 { + // do nothing + } + return *this; +} + +hpack& hpack::encode_string(binary_t& target, uint32 flags, std::string const& value) { return encode_string(target, flags, value.c_str(), value.size()); } + +hpack& hpack::encode_index(binary_t& target, uint8 index) { + // RFC 7541 Figure 5: Indexed Header Field + // + // 0 1 2 3 4 5 6 7 + // +---+---+---+---+---+---+---+---+ + // | 1 | Index (7+) | + // +---+---------------------------+ + + encode_int(target, 0x80, 7, index); + return *this; +} + +hpack& hpack::encode_indexed_name(binary_t& target, uint32 flags, uint8 index, const char* value) { + __try2 { + if (nullptr == value) { + __leave2; + } + encode_indexed_name(target, flags, index, value, strlen(value)); + } + __finally2 { + // do nothing + } + return *this; +} + +hpack& hpack::encode_indexed_name(binary_t& target, uint32 flags, uint8 index, const char* value, size_t size) { + __try2 { + if (nullptr == value) { + __leave2; + } + + if (hpack_indexing & flags) { + // RFC 7541 Figure 6: Literal Header Field with Incremental Indexing -- Indexed Name + // + // 0 1 2 3 4 5 6 7 + // +---+---+---+---+---+---+---+---+ + // | 0 | 1 | Index (6+) | + // +---+---+-----------------------+ + encode_int(target, 0x40, 6, index); + } else if (hpack_wo_indexing & flags) { + // RFC 7541 Figure 8: Literal Header Field without Indexing -- Indexed Name + // + // 0 1 2 3 4 5 6 7 + // +---+---+---+---+---+---+---+---+ + // | 0 | 0 | 0 | 0 | Index (4+) | + // +---+---+-----------------------+ + encode_int(target, 0x00, 4, index); + } else if (hpack_never_indexed & flags) { + // RFC 7541 Figure 10: Literal Header Field Never Indexed -- Indexed Name + // + // 0 1 2 3 4 5 6 7 + // +---+---+---+---+---+---+---+---+ + // | 0 | 0 | 0 | 1 | Index (4+) | + // +---+---+-----------------------+ + encode_int(target, 0x10, 4, index); + } else { + __leave2; + } + + // +-------------------------------+ + // | H | Value Length (7+) | + // +---+---------------------------+ + // | Value String (Length octets) | + // +-------------------------------+ + encode_string(target, flags, value, size); + } + __finally2 { + // do nothing + } + return *this; +} + +hpack& hpack::encode_indexed_name(binary_t& target, uint32 flags, uint8 index, std::string const& value) { + return encode_indexed_name(target, flags, index, value.c_str(), value.size()); +} + +hpack& hpack::encode_name_value(binary_t& target, uint32 flags, const char* name, const char* value) { + __try2 { + if (nullptr == name || nullptr == value) { + __leave2; + } + + if (hpack_indexing & flags) { + // RFC 7541 Figure 7: Literal Header Field with Incremental Indexing -- New Name + // + // 0 1 2 3 4 5 6 7 + // +---+---+---+---+---+---+---+---+ + // | 0 | 1 | 0 | + // +---+---+-----------------------+ + target.insert(target.end(), 0x40); + } else if (hpack_wo_indexing & flags) { + // RFC 7541 Figure 9: Literal Header Field without Indexing -- New Name + // + // 0 1 2 3 4 5 6 7 + // +---+---+---+---+---+---+---+---+ + // | 0 | 0 | 0 | 0 | 0 | + // +---+---+-----------------------+ + target.insert(target.end(), 0); + } else if (hpack_never_indexed & flags) { + // RFC 7541 Figure 11: Literal Header Field Never Indexed -- New Name + // + // 0 1 2 3 4 5 6 7 + // +---+---+---+---+---+---+---+---+ + // | 0 | 0 | 0 | 1 | 0 | + // +---+---+-----------------------+ + target.insert(target.end(), 0x10); + } else { + __leave2; + } + + // +---+---------------------------+ + // | H | Name Length (7+) | + // +---+---------------------------+ + // | Name String (Length octets) | + // +---+---------------------------+ + // | H | Value Length (7+) | + // +---+---------------------------+ + // | Value String (Length octets) | + // +-------------------------------+ + encode_string(target, flags, name); + encode_string(target, flags, value); + } + __finally2 { + // do nothing + } + return *this; +} + +hpack& hpack::encode_name_value(binary_t& target, uint32 flags, std::string const& name, std::string const& value) { + return encode_name_value(target, flags, name.c_str(), value.c_str()); +} + +hpack& hpack::encode_dyntablesize(binary_t& target, uint8 maxsize) { + // RFC 7541 Figure 12: Maximum Dynamic Table Size Change + encode_int(target, 0x20, 5, maxsize); + return *this; +} + +return_t hpack::decode_int(byte_t* p, size_t& pos, uint8 prefix, size_t& value) { + // 5.1. Integer Representation + // C.1. Integer Representation Examples + return_t ret = errorcode_t::success; + __try2 { + if (nullptr == p) { + ret = errorcode_t::success; + __leave2; + } + + value = 0; + if ((1 <= prefix) && (prefix <= 8)) { + uint8 n = (1 << prefix) - 1; + uint8 b = p[pos++]; + if (b < n) { + value = b; + } else { + size_t m = 0; + size_t i = b; + do { + b = p[pos++]; + i += (b & 0x7f) << m; + m += 7; + } while (0x80 == (b & 0x80)); + value = i; + } + } + } + __finally2 { + // do nothing + } + return ret; +} + +int hpack::find_table(std::string const& name, std::string const& value, size_t& index) { + int state = not_matched; + index = 0; + + { + size_t idx = 0; + for (dynamic_table_t::iterator iter = _dynamic_table.begin(); iter != _dynamic_table.end(); iter++, idx++) { + if ((name == iter->first) && (value == iter->second.value)) { + state = all_matched; + index = _static_table.size() + 1 + idx; + break; + } + } + } + if (not_matched == state) { + static_table_t::iterator iter; + static_table_t::iterator liter; + static_table_t::iterator uiter; + + liter = _static_table.lower_bound(name); + uiter = _static_table.upper_bound(name); + + for (iter = liter; iter != uiter; iter++) { + if (iter == liter) { + index = iter->second.index; // :path: /sample/path + state = key_matched; + } + if (value == iter->second.value) { + index = iter->second.index; + state = all_matched; + break; + } + } + } + return state; +} + +return_t hpack::insert_table(std::string const& name, std::string const& value) { + // RFC 7541 Figure 1: Index Address Space + // + // <---------- Index Address Space ----------> + // <-- Static Table --> <-- Dynamic Table --> + // +---+-----------+---+ +---+-----------+---+ + // | 1 | ... | s | |s+1| ... |s+k| + // +---+-----------+---+ +---+-----------+---+ + // ^ | + // | V + // Insertion Point Dropping Point + // + // Figure 1: Index Address Space + return_t ret = errorcode_t::success; + _dynamic_table.push_front(std::make_pair(name, http2_table_t(value, 0))); + return ret; +} + +hpack& hpack::encode_header(binary_t& target, std::string const& name, std::string const& value, uint32 flags) { + // RFC 7541 Appendix A. Static Table Definition + if (_static_table.empty()) { +#define ENTRY(index, header_name, header_value) \ + { index, header_name, header_value } + struct static_table_entry { + uint32 index; + const char* name; + const char* value; + } entries[] = { + ENTRY(1, ":authority", nullptr), + ENTRY(2, ":method", "GET"), + ENTRY(3, ":method", "POST"), + ENTRY(4, ":path", "/"), + ENTRY(5, ":path", "/index.html"), + ENTRY(6, ":scheme", "http"), + ENTRY(7, ":scheme", "https"), + ENTRY(8, ":status", "200"), + ENTRY(9, ":status", "204"), + ENTRY(10, ":status", "206"), + ENTRY(11, ":status", "304"), + ENTRY(12, ":status", "400"), + ENTRY(13, ":status", "404"), + ENTRY(14, ":status", "500"), + ENTRY(15, "accept-charset", nullptr), + ENTRY(16, "accept-encoding", "gzip,deflate"), + ENTRY(17, "accept-language", nullptr), + ENTRY(18, "accept-ranges", nullptr), + ENTRY(19, "accept", nullptr), + ENTRY(20, "access-control-allow-origin", nullptr), + ENTRY(21, "age", nullptr), + ENTRY(22, "allow", nullptr), + ENTRY(23, "authorization", nullptr), + ENTRY(24, "cache-control", nullptr), + ENTRY(25, "content-disposition", nullptr), + ENTRY(26, "content-encoding", nullptr), + ENTRY(27, "content-language", nullptr), + ENTRY(28, "content-length", nullptr), + ENTRY(29, "content-location", nullptr), + ENTRY(30, "content-range", nullptr), + ENTRY(31, "content-type", nullptr), + ENTRY(32, "cookie", nullptr), + ENTRY(33, "date", nullptr), + ENTRY(34, "etag", nullptr), + ENTRY(35, "expect", nullptr), + ENTRY(36, "expires", nullptr), + ENTRY(37, "from", nullptr), + ENTRY(38, "host", nullptr), + ENTRY(39, "if-match", nullptr), + ENTRY(40, "if-modified-since", nullptr), + ENTRY(41, "if-none-match", nullptr), + ENTRY(42, "if-range", nullptr), + ENTRY(43, "if-unmodified-since", nullptr), + ENTRY(44, "last-modified", nullptr), + ENTRY(45, "link", nullptr), + ENTRY(46, "location", nullptr), + ENTRY(47, "max-forwards", nullptr), + ENTRY(48, "proxy-authenticate", nullptr), + ENTRY(49, "proxy-authorization", nullptr), + ENTRY(50, "range", nullptr), + ENTRY(51, "referer", nullptr), + ENTRY(52, "refresh", nullptr), + ENTRY(53, "retry-after", nullptr), + ENTRY(54, "server", nullptr), + ENTRY(55, "set-cookie", nullptr), + ENTRY(56, "strict-transport-security", nullptr), + ENTRY(57, "transfer-encoding", nullptr), + ENTRY(58, "user-agent", nullptr), + ENTRY(59, "vary", nullptr), + ENTRY(60, "via", nullptr), + ENTRY(61, "www-authenticate", nullptr), + }; + + for (size_t i = 0; i < RTL_NUMBER_OF(entries); i++) { + static_table_entry* item = entries + i; + _static_table.insert(std::make_pair(item->name, http2_table_t(item->value, item->index))); + } + } + + enum { + not_matched = 1, + key_matched = 2, + all_matched = 3, + }; + int state = not_matched; + + size_t index = 0; + state = find_table(name, value, index); + + switch (state) { + case all_matched: + encode_index(target, index); + break; + case key_matched: + encode_indexed_name(target, flags, index, value); + if ((hpack_wo_indexing | hpack_never_indexed) & flags) { + // do nothing + } else { + insert_table(name, value); + } + break; + default: + if (hpack_wo_indexing & flags) { + encode_indexed_name(target, flags, index, value); + } else if (hpack_wo_indexing & flags) { + encode_name_value(target, flags, name, value); + } else { + encode_name_value(target, flags, name, value); + insert_table(name, value); + } + break; + } + return *this; +} } // namespace net } // namespace hotplace diff --git a/sdk/net/http/http2/hpack.hpp b/sdk/net/http/http2/hpack.hpp index fbd0a598..803cd074 100644 --- a/sdk/net/http/http2/hpack.hpp +++ b/sdk/net/http/http2/hpack.hpp @@ -12,14 +12,75 @@ #define __HOTPLACE_SDK_NET_HTTP_HTTP2_HPACK__ #include -#include -#include -#include namespace hotplace { namespace net { -// study +// RFC 7541 HPACK: Header Compression for HTTP/2 + +enum hpack_flag_t { + hpack_huffman = 1 << 0, + hpack_indexing = 1 << 1, + hpack_wo_indexing = 1 << 2, + hpack_never_indexed = 1 << 3, +}; + +class hpack { + public: + hpack(); + + hpack& encode_int(binary_t& target, uint8 mask, uint8 prefix, size_t value); + + hpack& encode_string(binary_t& target, uint32 flags, const char* value); + hpack& encode_string(binary_t& target, uint32 flags, const char* value, size_t size); + hpack& encode_string(binary_t& target, uint32 flags, std::string const& value); + + hpack& encode_index(binary_t& target, uint8 index); + + hpack& encode_indexed_name(binary_t& target, uint32 flags, uint8 index, const char* value); + hpack& encode_indexed_name(binary_t& target, uint32 flags, uint8 index, const char* value, size_t size); + hpack& encode_indexed_name(binary_t& target, uint32 flags, uint8 index, std::string const& value); + + hpack& encode_name_value(binary_t& target, uint32 flags, const char* name, const char* value); + hpack& encode_name_value(binary_t& target, uint32 flags, std::string const& name, std::string const& value); + + hpack& encode_dyntablesize(binary_t& target, uint8 maxsize); + + return_t decode_int(byte_t* p, size_t& pos, uint8 prefix, size_t& value); + + hpack& encode_header(binary_t& target, std::string const& name, std::string const& value, uint32 flags = 0); + + protected: + private: + enum { + not_matched = 1, + key_matched = 2, + all_matched = 3, + }; + typedef struct _http2_table_t { + std::string value; + size_t index; + _http2_table_t(size_t i) : index(i) {} + _http2_table_t(const char* v, size_t i = 0) : index(i) { + if (v) { + value = v; + } + } + _http2_table_t(std::string const& v, size_t i) : value(v), index(i) {} + } http2_table_t; + typedef std::multimap static_table_t; + typedef std::list> dynamic_table_t; + + huffman_coding _hc; + static_table_t _static_table; + dynamic_table_t _dynamic_table; + + int find_table(std::string const& name, std::string const& value, size_t& index); + return_t insert_table(std::string const& name, std::string const& value); +}; + +// RFC 7541 Appendix B. Huffman Code +extern const huffman_coding::hc_code_t _h2hcodes[]; } // namespace net } // namespace hotplace diff --git a/sdk/net/http/http2/http2_huffman_codes.cpp b/sdk/net/http/http2/http2_huffman_codes.cpp new file mode 100644 index 00000000..af7e66d9 --- /dev/null +++ b/sdk/net/http/http2/http2_huffman_codes.cpp @@ -0,0 +1,270 @@ +#include + +namespace hotplace { +namespace net { + +#define H2HC_ENTRY(s, c) \ + { s, c, } + +const huffman_coding::hc_code_t _h2hcodes[] = { + H2HC_ENTRY(1, "11111111111111111011000"), + H2HC_ENTRY(2, "1111111111111111111111100010"), + H2HC_ENTRY(3, "1111111111111111111111100011"), + H2HC_ENTRY(4, "1111111111111111111111100100"), + H2HC_ENTRY(5, "1111111111111111111111100101"), + H2HC_ENTRY(6, "1111111111111111111111100110"), + H2HC_ENTRY(7, "1111111111111111111111100111"), + H2HC_ENTRY(8, "1111111111111111111111101000"), + H2HC_ENTRY(9, "111111111111111111101010"), + H2HC_ENTRY(10, "111111111111111111111111111100"), + H2HC_ENTRY(11, "1111111111111111111111101001"), + H2HC_ENTRY(12, "1111111111111111111111101010"), + H2HC_ENTRY(13, "111111111111111111111111111101"), + H2HC_ENTRY(14, "1111111111111111111111101011"), + H2HC_ENTRY(15, "1111111111111111111111101100"), + H2HC_ENTRY(16, "1111111111111111111111101101"), + H2HC_ENTRY(17, "1111111111111111111111101110"), + H2HC_ENTRY(18, "1111111111111111111111101111"), + H2HC_ENTRY(19, "1111111111111111111111110000"), + H2HC_ENTRY(20, "1111111111111111111111110001"), + H2HC_ENTRY(21, "1111111111111111111111110010"), + H2HC_ENTRY(22, "111111111111111111111111111110"), + H2HC_ENTRY(23, "1111111111111111111111110011"), + H2HC_ENTRY(24, "1111111111111111111111110100"), + H2HC_ENTRY(25, "1111111111111111111111110101"), + H2HC_ENTRY(26, "1111111111111111111111110110"), + H2HC_ENTRY(27, "1111111111111111111111110111"), + H2HC_ENTRY(28, "1111111111111111111111111000"), + H2HC_ENTRY(29, "1111111111111111111111111001"), + H2HC_ENTRY(30, "1111111111111111111111111010"), + H2HC_ENTRY(31, "1111111111111111111111111011"), + H2HC_ENTRY(32, "010100"), // ' ' + H2HC_ENTRY(33, "1111111000"), // '!' + H2HC_ENTRY(34, "1111111001"), // '"' + H2HC_ENTRY(35, "111111111010"), // '#' + H2HC_ENTRY(36, "1111111111001"), // '$' + H2HC_ENTRY(37, "010101"), // '%' + H2HC_ENTRY(38, "11111000"), // '&' + H2HC_ENTRY(39, "11111111010"), // ''' + H2HC_ENTRY(40, "1111111010"), // '(' + H2HC_ENTRY(41, "1111111011"), // ')' + H2HC_ENTRY(42, "11111001"), // '*' + H2HC_ENTRY(43, "11111111011"), // '+' + H2HC_ENTRY(44, "11111010"), // ',' + H2HC_ENTRY(45, "010110"), // '-' + H2HC_ENTRY(46, "010111"), // '.' + H2HC_ENTRY(47, "011000"), // '/' + H2HC_ENTRY(48, "00000"), // '0' + H2HC_ENTRY(49, "00001"), // '1' + H2HC_ENTRY(50, "00010"), // '2' + H2HC_ENTRY(51, "011001"), // '3' + H2HC_ENTRY(52, "011010"), // '4' + H2HC_ENTRY(53, "011011"), // '5' + H2HC_ENTRY(54, "011100"), // '6' + H2HC_ENTRY(55, "011101"), // '7' + H2HC_ENTRY(56, "011110"), // '8' + H2HC_ENTRY(57, "011111"), // '9' + H2HC_ENTRY(58, "1011100"), // ':' + H2HC_ENTRY(59, "11111011"), // ';' + H2HC_ENTRY(60, "111111111111100"), // '<' + H2HC_ENTRY(61, "100000"), // '=' + H2HC_ENTRY(62, "111111111011"), // '>' + H2HC_ENTRY(63, "1111111100"), // '?' + H2HC_ENTRY(64, "1111111111010"), // '@' + H2HC_ENTRY(65, "100001"), // 'A' + H2HC_ENTRY(66, "1011101"), // 'B' + H2HC_ENTRY(67, "1011110"), // 'C' + H2HC_ENTRY(68, "1011111"), // 'D' + H2HC_ENTRY(69, "1100000"), // 'E' + H2HC_ENTRY(70, "1100001"), // 'F' + H2HC_ENTRY(71, "1100010"), // 'G' + H2HC_ENTRY(72, "1100011"), // 'H' + H2HC_ENTRY(73, "1100100"), // 'I' + H2HC_ENTRY(74, "1100101"), // 'J' + H2HC_ENTRY(75, "1100110"), // 'K' + H2HC_ENTRY(76, "1100111"), // 'L' + H2HC_ENTRY(77, "1101000"), // 'M' + H2HC_ENTRY(78, "1101001"), // 'N' + H2HC_ENTRY(79, "1101010"), // 'O' + H2HC_ENTRY(80, "1101011"), // 'P' + H2HC_ENTRY(81, "1101100"), // 'Q' + H2HC_ENTRY(82, "1101101"), // 'R' + H2HC_ENTRY(83, "1101110"), // 'S' + H2HC_ENTRY(84, "1101111"), // 'T' + H2HC_ENTRY(85, "1110000"), // 'U' + H2HC_ENTRY(86, "1110001"), // 'V' + H2HC_ENTRY(87, "1110010"), // 'W' + H2HC_ENTRY(88, "11111100"), // 'X' + H2HC_ENTRY(89, "1110011"), // 'Y' + H2HC_ENTRY(90, "11111101"), // 'Z' + H2HC_ENTRY(91, "1111111111011"), // '[' + H2HC_ENTRY(92, "1111111111111110000"), // '\' + H2HC_ENTRY(93, "1111111111100"), // ']' + H2HC_ENTRY(94, "11111111111100"), // '^' + H2HC_ENTRY(95, "100010"), // '_' + H2HC_ENTRY(96, "111111111111101"), // '`' + H2HC_ENTRY(97, "00011"), // 'a' + H2HC_ENTRY(98, "100011"), // 'b' + H2HC_ENTRY(99, "00100"), // 'c' + H2HC_ENTRY(100, "100100"), // 'd' + H2HC_ENTRY(101, "00101"), // 'e' + H2HC_ENTRY(102, "100101"), // 'f' + H2HC_ENTRY(103, "100110"), // 'g' + H2HC_ENTRY(104, "100111"), // 'h' + H2HC_ENTRY(105, "00110"), // 'i' + H2HC_ENTRY(106, "1110100"), // 'j' + H2HC_ENTRY(107, "1110101"), // 'k' + H2HC_ENTRY(108, "101000"), // 'l' + H2HC_ENTRY(109, "101001"), // 'm' + H2HC_ENTRY(110, "101010"), // 'n' + H2HC_ENTRY(111, "00111"), // 'o' + H2HC_ENTRY(112, "101011"), // 'p' + H2HC_ENTRY(113, "1110110"), // 'q' + H2HC_ENTRY(114, "101100"), // 'r' + H2HC_ENTRY(115, "01000"), // 's' + H2HC_ENTRY(116, "01001"), // 't' + H2HC_ENTRY(117, "101101"), // 'u' + H2HC_ENTRY(118, "1110111"), // 'v' + H2HC_ENTRY(119, "1111000"), // 'w' + H2HC_ENTRY(120, "1111001"), // 'x' + H2HC_ENTRY(121, "1111010"), // 'y' + H2HC_ENTRY(122, "1111011"), // 'z' + H2HC_ENTRY(123, "111111111111110"), // '{' + H2HC_ENTRY(124, "11111111100"), // '|' + H2HC_ENTRY(125, "11111111111101"), // '}' + H2HC_ENTRY(126, "1111111111101"), // '~' + H2HC_ENTRY(127, "1111111111111111111111111100"), + H2HC_ENTRY(128, "11111111111111100110"), + H2HC_ENTRY(129, "1111111111111111010010"), + H2HC_ENTRY(130, "11111111111111100111"), + H2HC_ENTRY(131, "11111111111111101000"), + H2HC_ENTRY(132, "1111111111111111010011"), + H2HC_ENTRY(133, "1111111111111111010100"), + H2HC_ENTRY(134, "1111111111111111010101"), + H2HC_ENTRY(135, "11111111111111111011001"), + H2HC_ENTRY(136, "1111111111111111010110"), + H2HC_ENTRY(137, "11111111111111111011010"), + H2HC_ENTRY(138, "11111111111111111011011"), + H2HC_ENTRY(139, "11111111111111111011100"), + H2HC_ENTRY(140, "11111111111111111011101"), + H2HC_ENTRY(141, "11111111111111111011110"), + H2HC_ENTRY(142, "111111111111111111101011"), + H2HC_ENTRY(143, "11111111111111111011111"), + H2HC_ENTRY(144, "111111111111111111101100"), + H2HC_ENTRY(145, "111111111111111111101101"), + H2HC_ENTRY(146, "1111111111111111010111"), + H2HC_ENTRY(147, "11111111111111111100000"), + H2HC_ENTRY(148, "111111111111111111101110"), + H2HC_ENTRY(149, "11111111111111111100001"), + H2HC_ENTRY(150, "11111111111111111100010"), + H2HC_ENTRY(151, "11111111111111111100011"), + H2HC_ENTRY(152, "11111111111111111100100"), + H2HC_ENTRY(153, "111111111111111011100"), + H2HC_ENTRY(154, "1111111111111111011000"), + H2HC_ENTRY(155, "11111111111111111100101"), + H2HC_ENTRY(156, "1111111111111111011001"), + H2HC_ENTRY(157, "11111111111111111100110"), + H2HC_ENTRY(158, "11111111111111111100111"), + H2HC_ENTRY(159, "111111111111111111101111"), + H2HC_ENTRY(160, "1111111111111111011010"), + H2HC_ENTRY(161, "111111111111111011101"), + H2HC_ENTRY(162, "11111111111111101001"), + H2HC_ENTRY(163, "1111111111111111011011"), + H2HC_ENTRY(164, "1111111111111111011100"), + H2HC_ENTRY(165, "11111111111111111101000"), + H2HC_ENTRY(166, "11111111111111111101001"), + H2HC_ENTRY(167, "111111111111111011110"), + H2HC_ENTRY(168, "11111111111111111101010"), + H2HC_ENTRY(169, "1111111111111111011101"), + H2HC_ENTRY(170, "1111111111111111011110"), + H2HC_ENTRY(171, "111111111111111111110000"), + H2HC_ENTRY(172, "111111111111111011111"), + H2HC_ENTRY(173, "1111111111111111011111"), + H2HC_ENTRY(174, "11111111111111111101011"), + H2HC_ENTRY(175, "11111111111111111101100"), + H2HC_ENTRY(176, "111111111111111100000"), + H2HC_ENTRY(177, "111111111111111100001"), + H2HC_ENTRY(178, "1111111111111111100000"), + H2HC_ENTRY(179, "111111111111111100010"), + H2HC_ENTRY(180, "11111111111111111101101"), + H2HC_ENTRY(181, "1111111111111111100001"), + H2HC_ENTRY(182, "11111111111111111101110"), + H2HC_ENTRY(183, "11111111111111111101111"), + H2HC_ENTRY(184, "11111111111111101010"), + H2HC_ENTRY(185, "1111111111111111100010"), + H2HC_ENTRY(186, "1111111111111111100011"), + H2HC_ENTRY(187, "1111111111111111100100"), + H2HC_ENTRY(188, "11111111111111111110000"), + H2HC_ENTRY(189, "1111111111111111100101"), + H2HC_ENTRY(190, "1111111111111111100110"), + H2HC_ENTRY(191, "11111111111111111110001"), + H2HC_ENTRY(192, "11111111111111111111100000"), + H2HC_ENTRY(193, "11111111111111111111100001"), + H2HC_ENTRY(194, "11111111111111101011"), + H2HC_ENTRY(195, "1111111111111110001"), + H2HC_ENTRY(196, "1111111111111111100111"), + H2HC_ENTRY(197, "11111111111111111110010"), + H2HC_ENTRY(198, "1111111111111111101000"), + H2HC_ENTRY(199, "1111111111111111111101100"), + H2HC_ENTRY(200, "11111111111111111111100010"), + H2HC_ENTRY(201, "11111111111111111111100011"), + H2HC_ENTRY(202, "11111111111111111111100100"), + H2HC_ENTRY(203, "111111111111111111111011110"), + H2HC_ENTRY(204, "111111111111111111111011111"), + H2HC_ENTRY(205, "11111111111111111111100101"), + H2HC_ENTRY(206, "111111111111111111110001"), + H2HC_ENTRY(207, "1111111111111111111101101"), + H2HC_ENTRY(208, "1111111111111110010"), + H2HC_ENTRY(209, "111111111111111100011"), + H2HC_ENTRY(210, "11111111111111111111100110"), + H2HC_ENTRY(211, "111111111111111111111100000"), + H2HC_ENTRY(212, "111111111111111111111100001"), + H2HC_ENTRY(213, "11111111111111111111100111"), + H2HC_ENTRY(214, "111111111111111111111100010"), + H2HC_ENTRY(215, "111111111111111111110010"), + H2HC_ENTRY(216, "111111111111111100100"), + H2HC_ENTRY(217, "111111111111111100101"), + H2HC_ENTRY(218, "11111111111111111111101000"), + H2HC_ENTRY(219, "11111111111111111111101001"), + H2HC_ENTRY(220, "1111111111111111111111111101"), + H2HC_ENTRY(221, "111111111111111111111100011"), + H2HC_ENTRY(222, "111111111111111111111100100"), + H2HC_ENTRY(223, "111111111111111111111100101"), + H2HC_ENTRY(224, "11111111111111101100"), + H2HC_ENTRY(225, "111111111111111111110011"), + H2HC_ENTRY(226, "11111111111111101101"), + H2HC_ENTRY(227, "111111111111111100110"), + H2HC_ENTRY(228, "1111111111111111101001"), + H2HC_ENTRY(229, "111111111111111100111"), + H2HC_ENTRY(230, "111111111111111101000"), + H2HC_ENTRY(231, "11111111111111111110011"), + H2HC_ENTRY(232, "1111111111111111101010"), + H2HC_ENTRY(233, "1111111111111111101011"), + H2HC_ENTRY(234, "1111111111111111111101110"), + H2HC_ENTRY(235, "1111111111111111111101111"), + H2HC_ENTRY(236, "111111111111111111110100"), + H2HC_ENTRY(237, "111111111111111111110101"), + H2HC_ENTRY(238, "11111111111111111111101010"), + H2HC_ENTRY(239, "11111111111111111110100"), + H2HC_ENTRY(240, "11111111111111111111101011"), + H2HC_ENTRY(241, "111111111111111111111100110"), + H2HC_ENTRY(242, "11111111111111111111101100"), + H2HC_ENTRY(243, "11111111111111111111101101"), + H2HC_ENTRY(244, "111111111111111111111100111"), + H2HC_ENTRY(245, "111111111111111111111101000"), + H2HC_ENTRY(246, "111111111111111111111101001"), + H2HC_ENTRY(247, "111111111111111111111101010"), + H2HC_ENTRY(248, "111111111111111111111101011"), + H2HC_ENTRY(249, "1111111111111111111111111110"), + H2HC_ENTRY(250, "111111111111111111111101100"), + H2HC_ENTRY(251, "111111111111111111111101101"), + H2HC_ENTRY(252, "111111111111111111111101110"), + H2HC_ENTRY(253, "111111111111111111111101111"), + H2HC_ENTRY(254, "111111111111111111111110000"), + H2HC_ENTRY(255, "11111111111111111111101110"), + // H2HC_ENTRY(256, "111111111111111111111111111111"), // EOS + H2HC_ENTRY(0, nullptr), +}; + +} // namespace net +} // namespace hotplace \ No newline at end of file diff --git a/test/base/sample.cpp b/test/base/sample.cpp index 13edf913..db153ad1 100644 --- a/test/base/sample.cpp +++ b/test/base/sample.cpp @@ -12,8 +12,6 @@ #include #include -#include -#include #include #include @@ -306,7 +304,7 @@ void test_btree() { _test_case.assert(15 == bt.size(), __FUNCTION__, "t_btree insert and update"); printf("members in [\n"); - bt.for_each([](testdata const& t) -> void { printf("%c %02x %u\n", isprint(t.symbol) ? t.symbol : '?', t.symbol, t.weight); }); + bt.for_each([](testdata const& t) -> void { printf("%c %02x %zi\n", isprint(t.symbol) ? t.symbol : '?', t.symbol, t.weight); }); printf("]\n"); } } @@ -356,6 +354,8 @@ void test_huffman_codes() { huff.encode(bin, (byte_t*)sample, strlen(sample)); dump_memory(bin, &bs); printf("%s\n", bs.c_str()); + + // to decode, min(code len in bits) MUST >= 5 } int main() { diff --git a/test/hpack/sample.cpp b/test/hpack/sample.cpp index b6feed30..152379ef 100644 --- a/test/hpack/sample.cpp +++ b/test/hpack/sample.cpp @@ -24,12 +24,9 @@ using namespace hotplace::net; test_case _test_case; typedef struct _OPTION { - std::string url; - int mode; - int connect; int verbose; - _OPTION() : url("https://localhost:9000/"), mode(0), connect(0), verbose(0) {} + _OPTION() : verbose(0) {} } OPTION; t_shared_instance > cmdline; @@ -45,218 +42,17 @@ void cprint(const char* text, ...) { std::cout << _concolor.turnoff() << std::endl; } -// RFC 7541 HPACK: Header Compression for HTTP/2 -// C.1. Integer Representation Examples -void hpack_encode_int(binary_t& target, uint8 prefix, int32 value, uint8 pattern = 0) { - // RFC 7541 5.1. Integer Representation - if ((1 <= prefix) && (prefix <= 8)) { - uint8 n = (1 << prefix) - 1; - uint8 i = 0; - if (value < n) { - target.insert(target.end(), value | pattern); - } else { - target.insert(target.end(), n | pattern); - value -= n; - while (value >= 128) { - i = (value % 128) + 128; - target.insert(target.end(), i); - value /= 128; - } - target.insert(target.end(), value); - } - } -} - -void hpack_decode_int(byte_t* p, size_t& pos, uint8 prefix, uint32& value) { - // RFC 7541 5.1. Integer Representation - value = 0; - if (p && (1 <= prefix) && (prefix <= 8)) { - uint8 n = (1 << prefix) - 1; - uint8 b = p[pos++]; - if (b < n) { - value = b; - } else { - uint32 m = 0; - uint32 i = b; - do { - b = p[pos++]; - i += (b & 0x7f) << m; - m += 7; - } while (0x80 == (b & 0x80)); - value = i; - } - } -} - -// RFC 7541 Figure 4: String Literal Representation -void hpack_encode_string(binary_t& target, std::string value) { - // H(1), Len(7) - hpack_encode_int(target, 7, value.size()); - target.insert(target.end(), value.begin(), value.end()); -} - -// RFC 7541 Figure 5: Indexed Header Field -void hpack_encode_index(binary_t& target, uint8 index) { hpack_encode_int(target, 7, index, 0x80); } - -enum hpack_flag_t { - hpack_indexed = 1, - hpack_wo_index = 2, - hpack_never_indexed = 3, -}; - -void hpack_encode_indexed_name(binary_t& target, hpack_flag_t flag, uint8 index, std::string const& value) { - __try2 { - if (hpack_indexed == flag) { - // RFC 7541 Figure 6: Literal Header Field with Incremental Indexing -- Indexed Name - hpack_encode_int(target, 6, index, 0x40); - } else if (hpack_wo_index == flag) { - // RFC 7541 Figure 8: Literal Header Field without Indexing -- Indexed Name - hpack_encode_int(target, 4, index); - } else if (hpack_never_indexed == flag) { - // RFC 7541 Figure 10: Literal Header Field Never Indexed -- Indexed Name - hpack_encode_int(target, 4, index, 0x10); - } else { - __leave2; - } - hpack_encode_int(target, 7, value.size()); - target.insert(target.end(), value.begin(), value.end()); - } - __finally2 { - // do nothing - } -} - -void hpack_encode_name_value(binary_t& target, hpack_flag_t flag, uint8 index, std::string const& name, std::string const& value) { - __try2 { - if (hpack_indexed == flag) { - // RFC 7541 Figure 7: Literal Header Field with Incremental Indexing -- New Name - target.insert(target.end(), 0x40); - } else if (hpack_wo_index == flag) { - // RFC 7541 Figure 9: Literal Header Field without Indexing -- New Name - target.insert(target.end(), 0); - } else if (hpack_never_indexed == flag) { - // RFC 7541 Figure 11: Literal Header Field Never Indexed -- New Name - target.insert(target.end(), 0x10); - } else { - __leave2; - } - hpack_encode_int(target, 7, name.size()); - target.insert(target.end(), name.begin(), name.end()); - hpack_encode_int(target, 7, value.size()); - target.insert(target.end(), value.begin(), value.end()); - } - __finally2 { - // do nothing - } -} - -// RFC 7541 Figure 12: Maximum Dynamic Table Size Change -void hpack_encode_dyntablesize(binary_t& target, uint8 maxsize) { hpack_encode_int(target, 5, maxsize, 0x20); } - -typedef struct _http2_table_t { - std::string value; - uint32 index; - _http2_table_t(uint32 i) : index(i) {} - _http2_table_t(std::string const& v, uint32 i) : value(v), index(i) {} -} http2_table_t; -typedef std::multimap table_t; - -table_t static_table; -table_t dynamic_table; -uint32 dynamic_table_entry_no = 62; - -enum { - not_matched = 1, - key_matched = 2, - all_matched = 3, -}; - -int find_table(table_t& table, std::string const& name, std::string const& value, uint32& index) { - int state = not_matched; - index = 0; - - table_t::iterator iter; - table_t::iterator liter; - table_t::iterator uiter; - - liter = table.lower_bound(name); - uiter = table.upper_bound(name); - - for (iter = liter; iter != uiter; iter++) { - if (iter == liter) { - index = iter->second.index; // :path: /sample/path - if (iter->second.value.empty()) { - state = key_matched; - break; - } - } - if (value == iter->second.value) { - index = iter->second.index; - state = all_matched; - break; - } - } - return state; -} - -void hpack_encode_header(binary_t& target, std::string const& name, std::string const& value, bool doindex = true) { - // RFC 7541 Appendix A. Static Table Definition - if (static_table.empty()) { - static_table.insert(std::make_pair(":authority", http2_table_t(1))); - static_table.insert(std::make_pair(":method", http2_table_t("GET", 2))); - static_table.insert(std::make_pair(":method", http2_table_t("POST", 3))); - static_table.insert(std::make_pair(":path", http2_table_t("/", 4))); - static_table.insert(std::make_pair(":path", http2_table_t("/index.html", 5))); - static_table.insert(std::make_pair(":scheme", http2_table_t("http", 6))); - static_table.insert(std::make_pair(":scheme", http2_table_t("https", 7))); - // ... - static_table.insert(std::make_pair("cache-control", http2_table_t(24))); - // ... - static_table.insert(std::make_pair("www-authenticate", http2_table_t(61))); - } - - enum { - not_matched = 1, - key_matched = 2, - all_matched = 3, - }; - int state = not_matched; - - uint32 index = 0; - state = find_table(dynamic_table, name, value, index); - if (not_matched == state) { - state = find_table(static_table, name, value, index); - } - - switch (state) { - case all_matched: - hpack_encode_index(target, index); - break; - case key_matched: - hpack_encode_indexed_name(target, hpack_indexed, index, value); - dynamic_table.insert(std::make_pair(name, http2_table_t(value, dynamic_table_entry_no++))); - break; - default: - if (doindex) { - hpack_encode_name_value(target, hpack_indexed, dynamic_table_entry_no++, name, value); - dynamic_table.insert(std::make_pair(name, http2_table_t(value, dynamic_table_entry_no++))); - } else { - hpack_encode_indexed_name(target, hpack_wo_index, index, value); - } - break; - } -} - -void test_rfc7541_c_1_routine(uint8 prefix, int32 i, const char* expect, const char* text) { +void test_rfc7541_c_1_routine(uint8 prefix, size_t i, const char* expect, const char* text) { OPTION& option = cmdline->value(); + hpack hp; binary_t bin; basic_stream bs; - uint32 value = 0; + size_t value = 0; size_t pos = 0; - hpack_encode_int(bin, prefix, i); - hpack_decode_int(&bin[0], pos, prefix, value); + hp.encode_int(bin, 0x00, prefix, i); + hp.decode_int(&bin[0], pos, prefix, value); if (option.verbose) { dump_memory(bin, &bs, 16, 2); @@ -267,74 +63,81 @@ void test_rfc7541_c_1_routine(uint8 prefix, int32 i, const char* expect, const c bool test = false; test = (expect && (i == value)); if (test) { - binary_t bin_expect = base16_decode(expect); + binary_t bin_expect = base16_decode_rfc(expect); test = (test && (bin == bin_expect)); } _test_case.assert(test, __FUNCTION__, text); } void test_rfc7541_c_1() { - _test_case.begin("RFC 7541 HPACK"); + _test_case.begin("RFC 7541 HPACK C.1. Integer Representation Examples"); OPTION& option = cmdline->value(); - test_rfc7541_c_1_routine(5, 10, "0a", "RFC 7541 C.1.1. Example 1: Encoding 10 Using a 5-Bit Prefix"); - test_rfc7541_c_1_routine(5, 1337, "1f9a0a", "RFC 7541 C.1.2. Example 2: Encoding 1337 Using a 5-Bit Prefix"); - test_rfc7541_c_1_routine(8, 42, "2a", "RFC 7541 C.1.3. Example 3: Encoding 42 Starting at an Octet Boundary"); + test_rfc7541_c_1_routine(5, 10, "0a", "RFC 7541 C.1.1. Example 1: Encoding 10 Using a 5-Bit Prefix"); + test_rfc7541_c_1_routine(5, 1337, "1f9a0a", "RFC 7541 C.1.2. Example 2: Encoding 1337 Using a 5-Bit Prefix"); + test_rfc7541_c_1_routine(8, 42, "2a", "RFC 7541 C.1.3. Example 3: Encoding 42 Starting at an Octet Boundary"); } void test_rfc7541_c_2() { - _test_case.begin("RFC 7541 HPACK"); + _test_case.begin("RFC 7541 HPACK C.2. Header Field Representation Examples"); OPTION& option = cmdline->value(); + hpack hp; + binary_t bin; basic_stream bs; // C.2.1. Literal Header Field with Indexing // "custom-key: custom-header" bin.clear(); - hpack_encode_name_value(bin, hpack_indexed, 0, "custom-key", "custom-header"); + hp.encode_header(bin, "custom-key", "custom-header", hpack_indexing); if (option.verbose) { dump_memory(bin, &bs, 16, 2); printf("encode\n%s\n", bs.c_str()); } - _test_case.assert(bin == base16_decode("400A637573746F6D2D6B65790D637573746F6D2D686561646572"), __FUNCTION__, "RFC 7541 C.2.1"); + const char* expect1 = + "400a 6375 7374 6f6d 2d6b 6579 0d63 7573 " + "746f 6d2d 6865 6164 6572 "; + _test_case.assert(bin == base16_decode_rfc(expect1), __FUNCTION__, "RFC 7541 C.2.1 Literal Header Field with Indexing"); // C.2.2. Literal Header Field without Indexing // :path: /sample/path bin.clear(); - hpack_encode_header(bin, ":path", "/sample/path", false); + hp.encode_header(bin, ":path", "/sample/path", hpack_wo_indexing); if (option.verbose) { dump_memory(bin, &bs, 16, 2); printf("encode\n%s\n", bs.c_str()); } - _test_case.assert(bin == base16_decode("040C2F73616D706C652F70617468"), __FUNCTION__, "RFC 7541 C.2.2"); + const char* expect2 = "040c 2f73 616d 706c 652f 7061 7468"; + _test_case.assert(bin == base16_decode_rfc(expect2), __FUNCTION__, "RFC 7541 C.2.2 Literal Header Field without Indexing"); // C.2.3. Literal Header Field Never Indexed // password: secret bin.clear(); - hpack_encode_name_value(bin, hpack_never_indexed, 0, "password", "secret"); + hp.encode_header(bin, "password", "secret", hpack_never_indexed); if (option.verbose) { dump_memory(bin, &bs, 16, 2); printf("encode\n%s\n", bs.c_str()); } - _test_case.assert(bin == base16_decode("100870617373776f726406736563726574"), __FUNCTION__, "RFC 7541 C.2.3"); + const char* expect3 = + "1008 7061 7373 776f 7264 0673 6563 7265 " + "74 "; + _test_case.assert(bin == base16_decode_rfc(expect3), __FUNCTION__, "RFC 7541 C.2.3 Literal Header Field Never Indexed"); // C.2.4. Indexed Header Field bin.clear(); - hpack_encode_header(bin, ":method", "GET"); + hp.encode_header(bin, ":method", "GET"); if (option.verbose) { dump_memory(bin, &bs, 16, 2); printf("encode\n%s\n", bs.c_str()); } - _test_case.assert(bin == base16_decode("82"), __FUNCTION__, "RFC 7541 C.2.4"); + const char* expect4 = "82"; + _test_case.assert(bin == base16_decode_rfc(expect4), __FUNCTION__, "RFC 7541 C.2.4 Indexed Header Field"); } // C.3. Request Examples without Huffman Coding void test_rfc7541_c_3() { - _test_case.begin("RFC 7541 HPACK"); + _test_case.begin("RFC 7541 HPACK C.3. Request Examples without Huffman Coding"); OPTION& option = cmdline->value(); - // reset ! - dynamic_table.clear(); - dynamic_table_entry_no = 62; - + hpack hp; binary_t bin; basic_stream bs; // C.3.1. First Request @@ -342,15 +145,18 @@ void test_rfc7541_c_3() { // :scheme: http // :path: / // :authority: www.example.com - hpack_encode_header(bin, ":method", "GET"); - hpack_encode_header(bin, ":scheme", "http"); - hpack_encode_header(bin, ":path", "/"); - hpack_encode_header(bin, ":authority", "www.example.com"); + hp.encode_header(bin, ":method", "GET"); + hp.encode_header(bin, ":scheme", "http"); + hp.encode_header(bin, ":path", "/"); + hp.encode_header(bin, ":authority", "www.example.com", hpack_indexing); if (option.verbose) { dump_memory(bin, &bs, 16, 2); printf("encode\n%s\n", bs.c_str()); } - _test_case.assert(bin == base16_decode("828684410f7777772e6578616d706c652e636f6d"), __FUNCTION__, "RFC 7541 C.3.1"); + const char* expect1 = + "8286 8441 0f77 7777 2e65 7861 6d70 6c65 " + "2e63 6f6d "; + _test_case.assert(bin == base16_decode_rfc(expect1), __FUNCTION__, "RFC 7541 C.3.1 First Request"); // [ 1] (s = 57) :authority: www.example.com // Table size: 57 @@ -358,16 +164,17 @@ void test_rfc7541_c_3() { // C.3.2. Second Request bin.clear(); - hpack_encode_header(bin, ":method", "GET"); - hpack_encode_header(bin, ":scheme", "http"); - hpack_encode_header(bin, ":path", "/"); - hpack_encode_header(bin, ":authority", "www.example.com"); - hpack_encode_header(bin, "cache-control", "no-cache"); + hp.encode_header(bin, ":method", "GET"); + hp.encode_header(bin, ":scheme", "http"); + hp.encode_header(bin, ":path", "/"); + hp.encode_header(bin, ":authority", "www.example.com", hpack_indexing); + hp.encode_header(bin, "cache-control", "no-cache", hpack_indexing); if (option.verbose) { dump_memory(bin, &bs, 16, 2); printf("encode\n%s\n", bs.c_str()); } - _test_case.assert(bin == base16_decode("828684be58086e6f2d6361636865"), __FUNCTION__, "RFC 7541 C.3.2"); + const char* expect2 = "8286 84be 5808 6e6f 2d63 6163 6865"; + _test_case.assert(bin == base16_decode_rfc(expect2), __FUNCTION__, "RFC 7541 C.3.2 Second Request"); // [ 1] (s = 53) cache-control: no-cache // [ 2] (s = 57) :authority: www.example.com @@ -375,21 +182,20 @@ void test_rfc7541_c_3() { // C.3.3. Third Request - dynamic_table.clear(); - // dynamic_table.insert(std::make_pair("cache-control", http2_table_t("no-cache", 62))); - dynamic_table.insert(std::make_pair(":authority", http2_table_t("www.example.com", 63))); - bin.clear(); - hpack_encode_header(bin, ":method", "GET"); - hpack_encode_header(bin, ":scheme", "https"); - hpack_encode_header(bin, ":path", "/index.html"); - hpack_encode_header(bin, ":authority", "www.example.com"); - hpack_encode_header(bin, "custom-key", "custom-value"); + hp.encode_header(bin, ":method", "GET"); + hp.encode_header(bin, ":scheme", "https"); + hp.encode_header(bin, ":path", "/index.html"); + hp.encode_header(bin, ":authority", "www.example.com"); + hp.encode_header(bin, "custom-key", "custom-value", hpack_indexing); if (option.verbose) { dump_memory(bin, &bs, 16, 2); printf("encode\n%s\n", bs.c_str()); } - _test_case.assert(bin == base16_decode("828785BF400A637573746F6D2D6B65790C637573746F6D2D76616C7565"), __FUNCTION__, "RFC 7541 C.3.3"); + const char* expect3 = + "8287 85bf 400a 6375 7374 6f6d 2d6b 6579 " + "0c63 7573 746f 6d2d 7661 6c75 65 "; + _test_case.assert(bin == base16_decode_rfc(expect3), __FUNCTION__, "RFC 7541 C.3.3 Third Request"); // [ 1] (s = 54) custom-key: custom-value // [ 2] (s = 53) cache-control: no-cache @@ -397,37 +203,232 @@ void test_rfc7541_c_3() { // Table size: 164 } +void test_huffman_codes_routine(huffman_coding* obj, const char* sample, const char* expect, const char* text) { + if (obj && sample && expect && text) { + OPTION& option = cmdline->value(); + + return_t ret = errorcode_t::success; + basic_stream bs; + binary_t bin; + + obj->encode(&bs, (byte_t*)sample, strlen(sample)); + if (option.verbose) { + printf("%s\n", bs.c_str()); + } + + obj->encode(bin, (byte_t*)sample, strlen(sample)); + if (option.verbose) { + dump_memory(bin, &bs); + printf("%s\n", bs.c_str()); + } + + _test_case.assert(bin == base16_decode_rfc(expect), __FUNCTION__, "%s encode", text); + + bs.clear(); + ret = obj->decode(&bs, &bin[0], bin.size()); + if (option.verbose) { + printf("%s\n", bs.c_str()); + } + + _test_case.assert(((errorcode_t::success == ret) && (bs == basic_stream(sample))), __FUNCTION__, "%s decode", text); + } +} + +void test_huffman_codes() { + _test_case.begin("RFC 7541 Appendix B. Huffman Code"); + + huffman_coding huff; + huff.imports(_h2hcodes); // RFC 7541 Appendix B. Huffman Code + + struct huffman_coding_testvector { + const char* sample; + const char* expect; + const char* text; + } vector[] = { + {"www.example.com", "f1e3 c2e5 f23a 6ba0 ab90 f4ff", "data#1"}, // RFC 7541 C.4.1 + {"no-cache", "a8eb 1064 9cbf", "data#2"}, // RFC 7541 C.4.2 + {"custom-key", "25a8 49e9 5ba9 7d7f", "data#3"}, // RFC 7541 C.4.3 + {"custom-value", "25a8 49e9 5bb8 e8b4 bf", "data#4"}, // RFC 7541 C.4.3 + + {"still a man hears what he wants to hear and disregards the rest", + "424d450a0d4a4752939476214f138d2a4e553c0ea4a1449d49ca3b141d5229219161661d922144ce552c2a13", "data#5"}, + }; + for (size_t i = 0; i < RTL_NUMBER_OF(vector); i++) { + huffman_coding_testvector* item = vector + i; + test_huffman_codes_routine(&huff, item->sample, item->expect, item->text); + } +} + // C.4. Request Examples with Huffman Coding void test_rfc7541_c_4() { - _test_case.begin("RFC 7541 HPACK"); + _test_case.begin("RFC 7541 HPACK C.4. Request Examples with Huffman Coding"); OPTION& option = cmdline->value(); + hpack hp; + binary_t bin; + basic_stream bs; + // C.4.1. First Request + hp.encode_header(bin, ":method", "GET"); + hp.encode_header(bin, ":scheme", "http"); + hp.encode_header(bin, ":path", "/"); + hp.encode_header(bin, ":authority", "www.example.com", hpack_indexing | hpack_huffman); + if (option.verbose) { + dump_memory(bin, &bs, 16, 2); + printf("encode\n%s\n", bs.c_str()); + } + const char* expect1 = + "8286 8441 8cf1 e3c2 e5f2 3a6b a0ab 90f4 " + "ff "; + _test_case.assert(bin == base16_decode_rfc(expect1), __FUNCTION__, "RFC 7541 C.4.1 First Request"); // C.4.2. Second Request + bin.clear(); + hp.encode_header(bin, ":method", "GET"); + hp.encode_header(bin, ":scheme", "http"); + hp.encode_header(bin, ":path", "/"); + hp.encode_header(bin, ":authority", "www.example.com", hpack_indexing | hpack_huffman); + hp.encode_header(bin, "cache-control", "no-cache", hpack_indexing | hpack_huffman); + if (option.verbose) { + dump_memory(bin, &bs, 16, 2); + printf("encode\n%s\n", bs.c_str()); + } + const char* expect2 = "8286 84be 5886 a8eb 1064 9cbf"; + _test_case.assert(bin == base16_decode_rfc(expect2), __FUNCTION__, "RFC 7541 C.4.2 Second Request"); // C.4.3. Third Request + bin.clear(); + hp.encode_header(bin, ":method", "GET"); + hp.encode_header(bin, ":scheme", "https"); + hp.encode_header(bin, ":path", "/index.html"); + hp.encode_header(bin, ":authority", "www.example.com", hpack_indexing | hpack_huffman); + hp.encode_header(bin, "custom-key", "custom-value", hpack_indexing | hpack_huffman); + if (option.verbose) { + dump_memory(bin, &bs, 16, 2); + printf("encode\n%s\n", bs.c_str()); + } + const char* expect3 = + "8287 85bf 4088 25a8 49e9 5ba9 7d7f 8925 " + "a849 e95b b8e8 b4bf "; + _test_case.assert(bin == base16_decode_rfc(expect3), __FUNCTION__, "RFC 7541 C.4.3 Third Request"); } // C.5. Response Examples without Huffman Coding void test_rfc7541_c_5() { - _test_case.begin("RFC 7541 HPACK"); + _test_case.begin("RFC 7541 HPACK C.5. Response Examples without Huffman Coding"); OPTION& option = cmdline->value(); + hpack hp; + binary_t bin; + basic_stream bs; + // C.5.1. First Response + hp.encode_header(bin, ":status", "302", hpack_indexing); + hp.encode_header(bin, "cache-control", "private", hpack_indexing); + hp.encode_header(bin, "date", "Mon, 21 Oct 2013 20:13:21 GMT", hpack_indexing); + hp.encode_header(bin, "location", "https://www.example.com", hpack_indexing); + if (option.verbose) { + dump_memory(bin, &bs, 16, 2); + printf("encode\n%s\n", bs.c_str()); + } + const char* expect1 = + "4803 3330 3258 0770 7269 7661 7465 611d " + "4d6f 6e2c 2032 3120 4f63 7420 3230 3133 " + "2032 303a 3133 3a32 3120 474d 546e 1768 " + "7474 7073 3a2f 2f77 7777 2e65 7861 6d70 " + "6c65 2e63 6f6d "; + _test_case.assert(bin == base16_decode_rfc(expect1), __FUNCTION__, "RFC 7541 C.5.1 First Response"); // C.5.2. Second Response + bin.clear(); + hp.encode_header(bin, ":status", "307", hpack_indexing); + hp.encode_header(bin, "cache-control", "private", hpack_indexing); + hp.encode_header(bin, "date", "Mon, 21 Oct 2013 20:13:21 GMT", hpack_indexing); + hp.encode_header(bin, "location", "https://www.example.com", hpack_indexing); + if (option.verbose) { + dump_memory(bin, &bs, 16, 2); + printf("encode\n%s\n", bs.c_str()); + } + const char* expect2 = "4803 3330 37c1 c0bf"; + _test_case.assert(bin == base16_decode_rfc(expect2), __FUNCTION__, "RFC 7541 C.5.2 Second Response"); // C.5.3. Third Response + bin.clear(); + hp.encode_header(bin, ":status", "200", hpack_indexing); + hp.encode_header(bin, "cache-control", "private", hpack_indexing); + hp.encode_header(bin, "date", "Mon, 21 Oct 2013 20:13:22 GMT", hpack_indexing); + hp.encode_header(bin, "location", "https://www.example.com", hpack_indexing); + hp.encode_header(bin, "content-encoding", "gzip", hpack_indexing); + hp.encode_header(bin, "set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1", hpack_indexing); + if (option.verbose) { + dump_memory(bin, &bs, 16, 2); + printf("encode\n%s\n", bs.c_str()); + } + const char* expect3 = + "88c1 611d 4d6f 6e2c 2032 3120 4f63 7420 " + "3230 3133 2032 303a 3133 3a32 3220 474d " + "54c0 5a04 677a 6970 7738 666f 6f3d 4153 " + "444a 4b48 514b 425a 584f 5157 454f 5049 " + "5541 5851 5745 4f49 553b 206d 6178 2d61 " + "6765 3d33 3630 303b 2076 6572 7369 6f6e " + "3d31 "; + _test_case.assert(bin == base16_decode_rfc(expect3), __FUNCTION__, "RFC 7541 C.5.3 Third Response"); } // C.6. Response Examples with Huffman Coding void test_rfc7541_c_6() { - _test_case.begin("RFC 7541 HPACK"); + _test_case.begin("RFC 7541 HPACK C.6. Response Examples with Huffman Coding"); OPTION& option = cmdline->value(); + hpack hp; + binary_t bin; + basic_stream bs; + // C.6.1. First Response + hp.encode_header(bin, ":status", "302", hpack_indexing | hpack_huffman); + hp.encode_header(bin, "cache-control", "private", hpack_indexing | hpack_huffman); + hp.encode_header(bin, "date", "Mon, 21 Oct 2013 20:13:21 GMT", hpack_indexing | hpack_huffman); + hp.encode_header(bin, "location", "https://www.example.com", hpack_indexing | hpack_huffman); + if (option.verbose) { + dump_memory(bin, &bs, 16, 2); + printf("encode\n%s\n", bs.c_str()); + } + const char* expect1 = + "4882 6402 5885 aec3 771a 4b61 96d0 7abe " + "9410 54d4 44a8 2005 9504 0b81 66e0 82a6 " + "2d1b ff6e 919d 29ad 1718 63c7 8f0b 97c8 " + "e9ae 82ae 43d3 "; + _test_case.assert(bin == base16_decode_rfc(expect1), __FUNCTION__, "RFC 7541 C.6.1 First Response"); // C.6.2. Second Response + bin.clear(); + hp.encode_header(bin, ":status", "307", hpack_indexing | hpack_huffman); + hp.encode_header(bin, "cache-control", "private", hpack_indexing | hpack_huffman); + hp.encode_header(bin, "date", "Mon, 21 Oct 2013 20:13:21 GMT", hpack_indexing | hpack_huffman); + hp.encode_header(bin, "location", "https://www.example.com", hpack_indexing | hpack_huffman); + if (option.verbose) { + dump_memory(bin, &bs, 16, 2); + printf("encode\n%s\n", bs.c_str()); + } + const char* expect2 = "4883 640e ffc1 c0bf"; + _test_case.assert(bin == base16_decode_rfc(expect2), __FUNCTION__, "RFC 7541 C.6.2 Second Response"); // C.6.3. Third Response + bin.clear(); + hp.encode_header(bin, ":status", "200", hpack_indexing | hpack_huffman); + hp.encode_header(bin, "cache-control", "private", hpack_indexing | hpack_huffman); + hp.encode_header(bin, "date", "Mon, 21 Oct 2013 20:13:22 GMT", hpack_indexing | hpack_huffman); + hp.encode_header(bin, "location", "https://www.example.com", hpack_indexing | hpack_huffman); + hp.encode_header(bin, "content-encoding", "gzip", hpack_indexing | hpack_huffman); + hp.encode_header(bin, "set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1", hpack_indexing | hpack_huffman); + if (option.verbose) { + dump_memory(bin, &bs, 16, 2); + printf("encode\n%s\n", bs.c_str()); + } + const char* expect3 = + "88c1 6196 d07a be94 1054 d444 a820 0595 " + "040b 8166 e084 a62d 1bff c05a 839b d9ab " + "77ad 94e7 821d d7f2 e6c7 b335 dfdf cd5b " + "3960 d5af 2708 7f36 72c1 ab27 0fb5 291f " + "9587 3160 65c0 03ed 4ee5 b106 3d50 07 "; + _test_case.assert(bin == base16_decode_rfc(expect3), __FUNCTION__, "RFC 7541 C.6.3 Third Response"); } int main(int argc, char** argv) { @@ -443,10 +444,7 @@ int main(int argc, char** argv) { cmdline.make_share(new cmdline_t