Skip to content

Instantly share code, notes, and snippets.

@dptole
Created April 27, 2020 06:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dptole/5e4d950b97cad589af10d950d1f3bcf4 to your computer and use it in GitHub Desktop.
Save dptole/5e4d950b97cad589af10d950d1f3bcf4 to your computer and use it in GitHub Desktop.
Encode/Decode text using HPACK: Header Compression for HTTP/2 https://httpwg.org/specs/rfc7541.html
/*
Encode/Decode text using HPACK: Header Compression for HTTP/2.
https://httpwg.org/specs/rfc7541.html
dptole@github
*/
var hpack = {
getEos: function () {
return '111111111111111111111111111111';
},
getByteLength: function () {
return 8;
},
getLengthPrefix: function () {
return 0x80;
},
getTable: function () {
return [
'1111111111000',
'11111111111111111011000',
'1111111111111111111111100010',
'1111111111111111111111100011',
'1111111111111111111111100100',
'1111111111111111111111100101',
'1111111111111111111111100110',
'1111111111111111111111100111',
'1111111111111111111111101000',
'111111111111111111101010',
'111111111111111111111111111100',
'1111111111111111111111101001',
'1111111111111111111111101010',
'111111111111111111111111111101',
'1111111111111111111111101011',
'1111111111111111111111101100',
'1111111111111111111111101101',
'1111111111111111111111101110',
'1111111111111111111111101111',
'1111111111111111111111110000',
'1111111111111111111111110001',
'1111111111111111111111110010',
'111111111111111111111111111110',
'1111111111111111111111110011',
'1111111111111111111111110100',
'1111111111111111111111110101',
'1111111111111111111111110110',
'1111111111111111111111110111',
'1111111111111111111111111000',
'1111111111111111111111111001',
'1111111111111111111111111010',
'1111111111111111111111111011',
'010100',
'1111111000',
'1111111001',
'111111111010',
'1111111111001',
'010101',
'11111000',
'11111111010',
'1111111010',
'1111111011',
'11111001',
'11111111011',
'11111010',
'010110',
'010111',
'011000',
'00000',
'00001',
'00010',
'011001',
'011010',
'011011',
'011100',
'011101',
'011110',
'011111',
'1011100',
'11111011',
'111111111111100',
'100000',
'111111111011',
'1111111100',
'1111111111010',
'100001',
'1011101',
'1011110',
'1011111',
'1100000',
'1100001',
'1100010',
'1100011',
'1100100',
'1100101',
'1100110',
'1100111',
'1101000',
'1101001',
'1101010',
'1101011',
'1101100',
'1101101',
'1101110',
'1101111',
'1110000',
'1110001',
'1110010',
'11111100',
'1110011',
'11111101',
'1111111111011',
'1111111111111110000',
'1111111111100',
'11111111111100',
'100010',
'111111111111101',
'00011',
'100011',
'00100',
'100100',
'00101',
'100101',
'100110',
'100111',
'00110',
'1110100',
'1110101',
'101000',
'101001',
'101010',
'00111',
'101011',
'1110110',
'101100',
'01000',
'01001',
'101101',
'1110111',
'1111000',
'1111001',
'1111010',
'1111011',
'111111111111110',
'11111111100',
'11111111111101',
'1111111111101',
'1111111111111111111111111100',
'11111111111111100110',
'1111111111111111010010',
'11111111111111100111',
'11111111111111101000',
'1111111111111111010011',
'1111111111111111010100',
'1111111111111111010101',
'11111111111111111011001',
'1111111111111111010110',
'11111111111111111011010',
'11111111111111111011011',
'11111111111111111011100',
'11111111111111111011101',
'11111111111111111011110',
'111111111111111111101011',
'11111111111111111011111',
'111111111111111111101100',
'111111111111111111101101',
'1111111111111111010111',
'11111111111111111100000',
'111111111111111111101110',
'11111111111111111100001',
'11111111111111111100010',
'11111111111111111100011',
'11111111111111111100100',
'111111111111111011100',
'1111111111111111011000',
'11111111111111111100101',
'1111111111111111011001',
'11111111111111111100110',
'11111111111111111100111',
'111111111111111111101111',
'1111111111111111011010',
'111111111111111011101',
'11111111111111101001',
'1111111111111111011011',
'1111111111111111011100',
'11111111111111111101000',
'11111111111111111101001',
'111111111111111011110',
'11111111111111111101010',
'1111111111111111011101',
'1111111111111111011110',
'111111111111111111110000',
'111111111111111011111',
'1111111111111111011111',
'11111111111111111101011',
'11111111111111111101100',
'111111111111111100000',
'111111111111111100001',
'1111111111111111100000',
'111111111111111100010',
'11111111111111111101101',
'1111111111111111100001',
'11111111111111111101110',
'11111111111111111101111',
'11111111111111101010',
'1111111111111111100010',
'1111111111111111100011',
'1111111111111111100100',
'11111111111111111110000',
'1111111111111111100101',
'1111111111111111100110',
'11111111111111111110001',
'11111111111111111111100000',
'11111111111111111111100001',
'11111111111111101011',
'1111111111111110001',
'1111111111111111100111',
'11111111111111111110010',
'1111111111111111101000',
'1111111111111111111101100',
'11111111111111111111100010',
'11111111111111111111100011',
'11111111111111111111100100',
'111111111111111111111011110',
'111111111111111111111011111',
'11111111111111111111100101',
'111111111111111111110001',
'1111111111111111111101101',
'1111111111111110010',
'111111111111111100011',
'11111111111111111111100110',
'111111111111111111111100000',
'111111111111111111111100001',
'11111111111111111111100111',
'111111111111111111111100010',
'111111111111111111110010',
'111111111111111100100',
'111111111111111100101',
'11111111111111111111101000',
'11111111111111111111101001',
'1111111111111111111111111101',
'111111111111111111111100011',
'111111111111111111111100100',
'111111111111111111111100101',
'11111111111111101100',
'111111111111111111110011',
'11111111111111101101',
'111111111111111100110',
'1111111111111111101001',
'111111111111111100111',
'111111111111111101000',
'11111111111111111110011',
'1111111111111111101010',
'1111111111111111101011',
'1111111111111111111101110',
'1111111111111111111101111',
'111111111111111111110100',
'111111111111111111110101',
'11111111111111111111101010',
'11111111111111111110100',
'11111111111111111111101011',
'111111111111111111111100110',
'11111111111111111111101100',
'11111111111111111111101101',
'111111111111111111111100111',
'111111111111111111111101000',
'111111111111111111111101001',
'111111111111111111111101010',
'111111111111111111111101011',
'1111111111111111111111111110',
'111111111111111111111101100',
'111111111111111111111101101',
'111111111111111111111101110',
'111111111111111111111101111',
'111111111111111111111110000',
'11111111111111111111101110',
];
},
/*
hpack.encode('something') => ['87', '41', 'e9', '2a', '67', '35', '53', '7f']
*/
encode: function (text) {
var table = hpack.getTable();
var byteLength = hpack.getByteLength();
var bits = text.split('').map(function (letter, index) {
var charCode = letter.charCodeAt(0);
if (charCode > table.length) {
throw new Error('Bad character: Invalid character starting at "' + text.substr(index, byteLength) + '".');
}
return table[charCode];
}).reduce(function (acc, tableIndex) {
return acc += tableIndex.toString(2);
}, '');
var slots = Array.from({length: Math.ceil(bits.length / byteLength)}).map(function (_, index) {
return bits.substr(index * byteLength, byteLength);
});
buffer = slots.map(function (slot) {
if (slot.length !== byteLength) slot = slot.padEnd(byteLength, '1');
return parseInt(slot, 2);
});
buffer.unshift(buffer.length + hpack.getLengthPrefix());
return buffer.map(function (b) {
return b.toString(16);
});
},
/*
hpack.decode(['87', '41', 'e9', '2a', '67', '35', '53', '7f']) => 'something'
hpack.decode('\x87\x41\xe9\x2a\x67\x35\x53\x7f') => 'something'
*/
decode: function (buffer) {
var byteLength = hpack.getByteLength();
if (typeof buffer === 'string') {
buffer = buffer.split('').map(function (b) {
return b.charCodeAt(0).toString(16);
});
}
var table = hpack.getTable();
var eos = hpack.getEos();
var bufferHead = buffer[0];
var bufferTail = buffer.slice(1);
var expectedBufferLength = parseInt(bufferHead, 16) - hpack.getLengthPrefix();
var currentBufferLength = buffer.length - 1;
if (expectedBufferLength < 1) {
throw new Error('Bad encoding: The buffer length cannot be negative.');
}
if (expectedBufferLength !== currentBufferLength) {
throw new Error('Bad encoding: The buffer length should be "' + expectedBufferLength + '" but its actually "' + currentBufferLength + '".');
}
var bits = bufferTail.map(function (b) {
return parseInt(b, 16).toString(2).padStart(byteLength, '0');
}).join('');
var text = '';
for (var i = 0; i <= bits.length; i++) {
var index = table.indexOf(bits.substr(0, i));
if (~index) {
text += String.fromCharCode(index);
bits = bits.substr(i);
i = 0;
}
}
if (bits.length > 0 && (bits === eos || bits.length > 7 || eos.substr(-bits.length) !== bits)) {
throw new Error('Bad padding: Unable to decode text.');
}
return text;
}
}
@dptole
Copy link
Author

dptole commented May 16, 2020

hpack.decode('\x84\xaa\x63\x55\xe7') === 'nginx'

hpack.decode('\x8b\x84\x84\x2d\x69\x5b\x05\x44\x3c\x86\xaa\x6f') === 'Accept-Encoding'

hpack.decode('\x95\xc1\x51\x2c\xf5\x5a\x54\x86\x8a\x14\xdf\x39\x54\xdf\x39\xaa\x99\x1f\xc7\xf1\xfc\x7f') === 'Encrypt All The Things!!!'

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment