123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126 |
- module vibe.http.internal.http2.hpack.encoder;
- import vibe.http.internal.http2.hpack.tables;
- import vibe.http.internal.http2.hpack.huffman;
- import vibe.http.internal.http2.hpack.util;
- import std.range;
- import std.typecons;
- import std.conv;
- import std.array;
- void encode(R)(HTTP2HeaderTableField header, ref R dst, ref IndexingTable table, bool huffman = true)
- @safe
- {
- // try to encode as integer
- bool indexed = encodeInteger(header, dst, table, huffman);
- // if fail, encode as literal
- if(!indexed) encodeLiteral(header, dst, huffman);
- }
- /// encode a pure integer (present in table) or integer name + literal value
- private bool encodeInteger(R)(const HTTP2HeaderTableField header, ref R dst, ref IndexingTable table, bool huffman = true)
- @trusted
- {
- // check table for indexed headers
- size_t idx = 1;
- bool found = false;
- size_t partialFound = false;
- while(idx < table.size) {
- // encode both name / value as index
- auto h = table[idx];
- if(h.name == header.name && h.value == header.value) {
- found = true;
- partialFound = false;
- break;
- // encode name as index, value as literal
- } else if(h.name == header.name && h.value != header.value) {
- found = false;
- partialFound = idx;
- }
- idx++;
- }
- if(found) {
- if(idx < 127) { // can be fit in one octet
- dst.put(cast(ubyte)(idx ^ 128));
- } else { // must be split in multiple octets
- dst.put(cast(ubyte)255);
- idx -= 127;
- while (idx > 127) {
- dst.put(cast(ubyte)((idx % 128) ^ 128));
- idx = idx / 128;
- }
- dst.put(cast(ubyte)(idx & 127));
- }
- return true;
- } else if(partialFound) {
- // encode name as index ( always smaller than 64 )
- if(header.index) dst.put(cast(ubyte)((partialFound + 64) & 127));
- else if (header.neverIndex) dst.put(cast(ubyte)((partialFound + 16) & 31));
- else dst.put(cast(ubyte)(partialFound & 15));
- // encode value as literal
- encodeLiteralField(to!string(header.value), dst, huffman);
- return true;
- }
- return false;
- }
- /// encode a literal field depending on its indexing requirements
- private void encodeLiteral(R)(const HTTP2HeaderTableField header, ref R dst, bool huffman = true)
- @safe
- {
- if(header.index) dst.put(cast(ubyte)(64));
- else if(header.neverIndex) dst.put(cast(ubyte)(16));
- else dst.put(cast(ubyte)(0));
- encodeLiteralField(to!string(header.name), dst, huffman);
- encodeLiteralField(to!string(header.value), dst, huffman);
- }
- /// encode a field (name / value) using huffman or raw encoding
- private void encodeLiteralField(R)(string src, ref R dst, bool huffman = true) @safe
- {
- if(huffman) {
- encodeHuffman(src, dst);
- } else {
- auto blen = (src.length) & 127;
- dst.put(cast(ubyte)blen);
- dst.put(cast(ubyte[])(to!string(src).dup));
- }
- }
- unittest {
- // encode integer
- import vibe.internal.array : BatchBuffer;
- import vibe.http.common;
- auto table = IndexingTable(4096);
- BatchBuffer!(ubyte, 1) bres;
- bres.putN(1);
- ubyte[1] expected = [0x82];
- auto hint = HTTP2HeaderTableField(":method", HTTPMethod.GET);
- assert(encodeInteger(hint, bres, table));
- assert(bres.peekDst == expected);
- }
- unittest {
- // encode literal
- // custom-key: custom-header
- import vibe.internal.array : BatchBuffer;
- ubyte[26] lexpected = [0x40, 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b,
- 0x65, 0x79, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x68, 0x65, 0x61, 0x64,
- 0x65, 0x72];
- BatchBuffer!(ubyte, 26) lres;
- lres.putN(26);
- auto hlit = HTTP2HeaderTableField("custom-key", "custom-header");
- encodeLiteral(hlit, lres, false);
- assert(lres.peekDst == lexpected);
- }
|