Socket
Socket
Sign inDemoInstall

http2-protocol

Package Overview
Dependencies
0
Maintainers
1
Versions
13
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.6.0 to 0.7.0

17

HISTORY.md
Version history
===============
### 0.7.0 (2013-11-10) ###
* Upgrade to the latest draft: [draft-ietf-httpbis-http2-07][draft-07]
* [Tarball](https://github.com/molnarg/node-http2-protocol/archive/node-http2-protocol-0.7.0.tar.gz)
[draft-07]: http://tools.ietf.org/html/draft-ietf-httpbis-http2-07
### 0.6.0 (2013-11-09) ###
* Splitting out node-http2-protocol from node-http2
* The only exported class is `Endpoint`
* Versioning will be based on the implemented protocol version with a '0.' prefix
* [Tarball](https://github.com/molnarg/node-http2-protocol/archive/node-http2-protocol-0.6.0.tar.gz)
Version history as part of the [node-http](https://github.com/molnarg/node-http2) module
----------------------------------------------------------------------------------------
### 1.0.1 (2013-10-14) ###

@@ -5,0 +22,0 @@

1117

lib/compressor.js

@@ -16,5 +16,6 @@ // The implementation of the [HTTP/2 Header Compression][http2-compression] spec is separated from

// [node-objectmode]: http://nodejs.org/api/stream.html#stream_new_stream_readable_options
// [http2-compression]: http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-03
// [http2-compression]: http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-04
exports.HeaderTable = HeaderTable;
exports.HuffmanTable = HuffmanTable;
exports.HeaderSetCompressor = HeaderSetCompressor;

@@ -35,11 +36,16 @@ exports.HeaderSetDecompressor = HeaderSetDecompressor;

// The [Header Table][headertable] is a component used to associate headers to index values. It is
// basically an ordered list of `[name, value]` pairs, so it's implemented as a subclass of `Array`.
// [headertable]: http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-03#section-3.1.2
function HeaderTable(log, table, limit) {
var self = table.map(entryFromPair);
// The [Header Table] is a component used to associate headers to index values. It is basically an
// ordered list of `[name, value]` pairs, so it's implemented as a subclass of `Array`.
// In this implementation, the Header Table and the [Static Table] are handled as a single table.
// [Header Table]: http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-04#section-3.1.2
// [Static Table]: http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-04#appendix-B
function HeaderTable(log, limit) {
var self = HeaderTable.staticTable.map(entryFromPair);
self._log = log;
self._limit = limit || DEFAULT_HEADER_TABLE_LIMIT;
self._size = tableSize(self);
self._staticLength = self.length;
self._size = 0;
self._enforceLimit = HeaderTable.prototype._enforceLimit;
self.add = HeaderTable.prototype.add;
self.setSizeLimit = HeaderTable.prototype.setSizeLimit;
return self;

@@ -58,3 +64,3 @@ }

//
// [referenceset]: http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-03#section-3.1.3
// [referenceset]: http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-04#section-3.1.3
//

@@ -98,35 +104,38 @@ // Relations of the sets:

function tableSize(table) {
var size = 0;
for (var i = 0; i < table.length; i++) {
size += table[i]._size;
}
return size;
}
// The `add(index, entry)` can be used to [manage the header table][tablemgmt]:
// [tablemgmt]: http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-03#section-3.2.4
// [tablemgmt]: http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-04#section-3.3
//
// * if `index` is `Infinite` it pushes the new `entry` at the end of the table
// * otherwise, it replaces the entry with the given `index` with the new `entry`
// * it pushes the new `entry` at the beggining of the table
// * before doing such a modification, it has to be ensured that the header table size will stay
// lower than or equal to the header table size limit. To achieve this, repeatedly, the first
// entry of the header table is removed, until enough space is available for the modification.
HeaderTable.prototype.add = function(index, entry) {
var limit = this._limit - entry._size;
// lower than or equal to the header table size limit. To achieve this, entries are evicted from
// the end of the header table until the size of the header table is less than or equal to
// `(this._limit - entry.size)`, or until the table is empty.
//
// <---------- Index Address Space ---------->
// <-- Header Table --> <-- Static Table -->
// +---+-----------+---+ +---+-----------+---+
// | 0 | ... | k | |k+1| ... | n |
// +---+-----------+---+ +---+-----------+---+
// ^ |
// | V
// Insertion Point Drop Point
HeaderTable.prototype._enforceLimit = function _enforceLimit(limit) {
var droppedEntries = [];
while ((this._size > limit) && (this.length > 0)) {
var dropped = this.shift();
var dropPoint = this.length - this._staticLength;
while ((this._size > limit) && (dropPoint > 0)) {
dropPoint -= 1;
var dropped = this.splice(dropPoint, 1)[0];
this._size -= dropped._size;
droppedEntries.push(dropped);
droppedEntries[droppedEntries] = dropped;
}
return droppedEntries;
};
HeaderTable.prototype.add = function(entry) {
var limit = this._limit - entry._size;
var droppedEntries = this._enforceLimit(limit);
if (this._size <= limit) {
index -= droppedEntries.length;
if (index < 0) {
this.unshift(entry);
} else {
this.splice(index, 1, entry); // this is like push() if index is Infinity
}
this.unshift(entry);
this._size += entry._size;

@@ -138,73 +147,76 @@ }

// Initial header tables
// ---------------------
// The table size limit can be changed externally. In this case, the same eviction algorithm is used
HeaderTable.prototype.setSizeLimit = function setSizeLimit(limit) {
this._limit = limit;
this._enforceLimit(this._limit);
};
// ### [Initial request table][requesttable] ###
// [requesttable]: http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-03#appendix-B.1
HeaderTable.initialRequestTable = [
[ ':scheme' , 'http' ],
[ ':scheme' , 'https' ],
[ ':host' , '' ],
[ ':path' , '/' ],
[ ':method' , 'get' ],
[ 'accept' , '' ],
[ 'accept-charset' , '' ],
[ 'accept-encoding' , '' ],
[ 'accept-language' , '' ],
[ 'cookie' , '' ],
[ 'if-modified-since' , '' ],
[ 'user-agent' , '' ],
[ 'referer' , '' ],
[ 'authorization' , '' ],
[ 'allow' , '' ],
[ 'cache-control' , '' ],
[ 'connection' , '' ],
[ 'content-length' , '' ],
[ 'content-type' , '' ],
[ 'date' , '' ],
[ 'expect' , '' ],
[ 'from' , '' ],
[ 'if-match' , '' ],
[ 'if-none-match' , '' ],
[ 'if-range' , '' ],
[ 'if-unmodified-since' , '' ],
[ 'max-forwards' , '' ],
[ 'proxy-authorization' , '' ],
[ 'range' , '' ],
[ 'via' , '' ]
];
// [The Static Table](http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-04#appendix-B)
// ------------------
// [statictable]:http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-04#appendix-B
// ### [Initial response table][responsetable] ###
// [responsetable]: http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-03#appendix-B.2
HeaderTable.initialResponseTable = [
[ ':status' , '200' ],
[ 'age' , '' ],
[ 'cache-control' , '' ],
[ 'content-length' , '' ],
[ 'content-type' , '' ],
[ 'date' , '' ],
[ 'etag' , '' ],
[ 'expires' , '' ],
[ 'last-modified' , '' ],
[ 'server' , '' ],
[ 'set-cookie' , '' ],
[ 'vary' , '' ],
[ 'via' , '' ],
[ 'access-control-allow-origin' , '' ],
[ 'accept-ranges' , '' ],
[ 'allow' , '' ],
[ 'connection' , '' ],
[ 'content-disposition' , '' ],
[ 'content-encoding' , '' ],
[ 'content-language' , '' ],
[ 'content-location' , '' ],
[ 'content-range' , '' ],
[ 'link' , '' ],
[ 'location' , '' ],
[ 'proxy-authenticate' , '' ],
[ 'refresh' , '' ],
[ 'retry-after' , '' ],
[ 'strict-transport-security' , '' ],
[ 'transfer-encoding' , '' ],
[ 'www-authenticate' , '' ]
// The table is generated with feeding the table from the spec to the following sed command:
//
// sed -re "s/\s*\| [0-9]+\s*\| ([^ ]*)/ [ '\1'/g" -e "s/\|\s([^ ]*)/, '\1'/g" -e 's/ \|/],/g'
HeaderTable.staticTable = [
[ ':authority' , '' ],
[ ':method' , 'GET' ],
[ ':method' , 'POST' ],
[ ':path' , '/' ],
[ ':path' , '/index.html' ],
[ ':scheme' , 'http' ],
[ ':scheme' , 'https' ],
[ ':status' , '200' ],
[ ':status' , '500' ],
[ ':status' , '404' ],
[ ':status' , '403' ],
[ ':status' , '400' ],
[ ':status' , '401' ],
[ 'accept-charset' , '' ],
[ 'accept-encoding' , '' ],
[ 'accept-language' , '' ],
[ 'accept-ranges' , '' ],
[ 'accept' , '' ],
[ 'access-control-allow-origin' , '' ],
[ 'age' , '' ],
[ 'allow' , '' ],
[ 'authorization' , '' ],
[ 'cache-control' , '' ],
[ 'content-disposition' , '' ],
[ 'content-encoding' , '' ],
[ 'content-language' , '' ],
[ 'content-length' , '' ],
[ 'content-location' , '' ],
[ 'content-range' , '' ],
[ 'content-type' , '' ],
[ 'cookie' , '' ],
[ 'date' , '' ],
[ 'etag' , '' ],
[ 'expect' , '' ],
[ 'expires' , '' ],
[ 'from' , '' ],
[ 'if-match' , '' ],
[ 'if-modified-since' , '' ],
[ 'if-none-match' , '' ],
[ 'if-range' , '' ],
[ 'if-unmodified-since' , '' ],
[ 'last-modified' , '' ],
[ 'link' , '' ],
[ 'location' , '' ],
[ 'max-forwards' , '' ],
[ 'proxy-authenticate' , '' ],
[ 'proxy-authorization' , '' ],
[ 'range' , '' ],
[ 'referer' , '' ],
[ 'refresh' , '' ],
[ 'retry-after' , '' ],
[ 'server' , '' ],
[ 'set-cookie' , '' ],
[ 'strict-transport-security' , '' ],
[ 'transfer-encoding' , '' ],
[ 'user-agent' , '' ],
[ 'vary' , '' ],
[ 'via' , '' ],
[ 'www-authenticate' , '' ]
];

@@ -223,3 +235,3 @@

util.inherits(HeaderSetDecompressor, TransformStream);
function HeaderSetDecompressor(log, table) {
function HeaderSetDecompressor(log, table, huffmanTable) {
TransformStream.call(this, { objectMode: true });

@@ -229,2 +241,3 @@

this._table = table;
this._huffmanTable = huffmanTable;
this._chunks = [];

@@ -242,3 +255,3 @@ }

// `execute(rep)` executes the given [header representation][representation].
// [representation]: http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-03#section-3.1.5
// [representation]: http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-04#section-3.1.4

@@ -250,5 +263,3 @@ // The *JavaScript object representation* of a header representation:

// value: String || Integer, // string literal or index
// index: Integer // -1 : no indexing
// // 0 - ... : substitution indexing
// // Infinity : incremental indexing
// index: Boolean // with or without indexing
// }

@@ -259,7 +270,7 @@ //

// Indexed:
// { name: 2 , value: 2 , index: -1 }
// { name: 2 , value: 2 , index: false }
// Literal:
// { name: 2 , value: 'X', index: -1 } // without indexing
// { name: 2 , value: 'Y', index: Infinity } // incremental indexing
// { name: 'A', value: 'Z', index: 123 } // substitution indexing
// { name: 2 , value: 'X', index: false } // without indexing
// { name: 2 , value: 'Y', index: true } // with indexing
// { name: 'A', value: 'Z', index: true } // with indexing, literal name
HeaderSetDecompressor.prototype._execute = function _execute(rep) {

@@ -269,3 +280,3 @@ this._log.trace({ key: rep.name, value: rep.value, index: rep.index },

var index, entry, pair;
var entry, pair;

@@ -277,6 +288,12 @@ // * An _indexed representation_ corresponding to an entry _present_ in the reference set

// entails the following actions:
// * The header corresponding to the entry is emitted.
// * The entry is added to the reference set.
// * If referencing an element of the static table:
// * The header field corresponding to the referenced entry is emitted
// * The referenced static entry is added to the header table
// * A reference to this new header table entry is added to the reference set (except if
// this new entry didn't fit in the header table)
// * If referencing an element of the header table:
// * The header field corresponding to the referenced entry is emitted
// * The referenced header table entry is added to the reference set
if (typeof rep.value === 'number') {
index = rep.value;
var index = rep.value;
entry = this._table[index];

@@ -287,6 +304,12 @@

} else {
pair = entry.slice();
this.push(pair);
if (index >= this._table.length - this._table._staticLength) {
entry = entryFromPair(pair);
this._table.add(entry);
}
entry.reference = true;
entry.emitted = true;
pair = entry.slice();
this.push(pair);
}

@@ -300,3 +323,3 @@ }

// actions:
// * The header is added to the header table, at the location defined by the representation.
// * The header is added to the header table.
// * The new entry is added to the reference set.

@@ -310,8 +333,7 @@ else {

index = rep.index;
if (index !== -1) {
if (rep.index) {
entry = entryFromPair(pair);
entry.reference = true;
entry.emitted = true;
this._table.add(index, entry);
this._table.add(entry);
}

@@ -333,6 +355,6 @@

while (buffer.cursor < buffer.length) {
this._execute(HeaderSetDecompressor.header(buffer));
this._execute(HeaderSetDecompressor.header(buffer, this._huffmanTable));
}
// * [emits the reference set](http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-03#section-3.2.2)
// * [emits the reference set](http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-04#section-3.2.2)
for (var index = 0; index < this._table.length; index++) {

@@ -362,3 +384,3 @@ var entry = this._table[index];

util.inherits(HeaderSetCompressor, TransformStream);
function HeaderSetCompressor(log, table) {
function HeaderSetCompressor(log, table, huffmanTable) {
TransformStream.call(this, { objectMode: true });

@@ -368,2 +390,3 @@

this._table = table;
this._huffmanTable = huffmanTable;
this.push = TransformStream.prototype.push.bind(this);

@@ -377,3 +400,3 @@ }

if (!rep.chunks) {
rep.chunks = HeaderSetCompressor.header(rep);
rep.chunks = HeaderSetCompressor.header(rep, this._huffmanTable);
}

@@ -393,10 +416,10 @@ rep.chunks.forEach(this.push);

var nameMatch = -1, fullMatch = -1;
for (var index = 0; index < this._table.length; index++) {
entry = this._table[index];
for (var droppedIndex = 0; droppedIndex < this._table.length; droppedIndex++) {
entry = this._table[droppedIndex];
if (entry[0] === name) {
if (entry[1] === value) {
fullMatch = index;
fullMatch = droppedIndex;
break;
} else if (nameMatch === -1) {
nameMatch = index;
nameMatch = droppedIndex;
}

@@ -410,3 +433,4 @@ }

// * If the entry is outside the reference set, then a single indexed representation puts the
// entry into it and emits the header.
// entry into it and emits the header. Note that if the matched entry is in the static table,
// then it has to be added to the header table.
//

@@ -434,2 +458,6 @@ // * If it's already in the keep set, then 4 indexed representations are needed:

if (!entry.reference) {
if (fullMatch >= this._table.length - this._table._staticLength) {
entry = entryFromPair(pair);
this._table.add(entry);
}
this.send(rep);

@@ -464,18 +492,11 @@ entry.reference = true;

var insertIndex;
if (entry._size > this._table._limit / 2) {
insertIndex = -1;
} else if (nameMatch !== -1) {
insertIndex = nameMatch;
} else {
insertIndex = Infinity;
}
var indexing = (entry._size < this._table._limit / 2);
if (insertIndex !== -1) {
if (indexing) {
entry.reference = true;
var droppedEntries = this._table.add(insertIndex, entry);
for (index = 0; index < droppedEntries.length; index++) {
var dropped = droppedEntries[index];
var droppedEntries = this._table.add(entry);
for (droppedIndex in droppedEntries) {
var dropped = droppedEntries[droppedIndex];
if (dropped.keep) {
rep = { name: index, value: index, index: -1 };
rep = { name: droppedIndex, value: droppedIndex, index: false };
this.send(rep);

@@ -487,3 +508,3 @@ this.send(rep);

this.send({ name: (nameMatch !== -1) ? nameMatch : name, value: value, index: insertIndex });
this.send({ name: (nameMatch !== -1) ? nameMatch : name, value: value, index: indexing });
}

@@ -512,3 +533,3 @@

// [Detailed Format](http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-03#section-4)
// [Detailed Format](http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-04#section-4)
// -----------------

@@ -590,22 +611,681 @@

// ### Huffman Encoding ###
function HuffmanTable(table) {
function createTree(codes, position) {
if (codes.length === 1) {
return [table.indexOf(codes[0])];
}
else {
position = position || 0;
var zero = [];
var one = [];
for (var i = 0; i < codes.length; i++) {
var string = codes[i];
if (string[position] === '0') {
zero.push(string);
} else {
one.push(string);
}
}
return [createTree(zero, position + 1), createTree(one, position + 1)];
}
}
this.tree = createTree(table);
this.codes = table.map(function(bits) {
return parseInt(bits, 2);
});
this.lengths = table.map(function(bits) {
return bits.length;
});
}
HuffmanTable.prototype.encode = function encode(buffer) {
var result = [];
var space = 8;
function add(data) {
if (space === 8) {
result.push(data);
} else {
result[result.length - 1] |= data;
}
}
for (var i = 0; i < buffer.length; i++) {
var byte = buffer[i];
var code = this.codes[byte];
var length = this.lengths[byte];
while (length !== 0) {
if (space >= length) {
add(code << (space - length));
code = 0;
space -= length;
length = 0;
} else {
var shift = length - space;
var msb = code >> shift;
add(msb);
code -= msb << shift;
length -= space;
space = 0;
}
if (space === 0) {
space = 8;
}
}
}
if (space !== 8) {
add(this.codes[256] >> (this.lengths[256] - space));
}
return new Buffer(result);
}
HuffmanTable.prototype.decode = function decode(buffer) {
var result = [];
var subtree = this.tree;
for (var i = 0; i < buffer.length; i++) {
var byte = buffer[i];
for (var j = 0; j < 8; j++) {
var bit = (byte & 128) ? 1 : 0;
byte = byte << 1;
subtree = subtree[bit];
if (subtree.length === 1) {
result.push(subtree[0]);
subtree = this.tree;
}
}
}
return new Buffer(result);
}
// The initializer arrays for the Huffman tables are generated with feeding the tables from the
// spec to this sed command:
//
// sed -e "s/^.* [|]//g" -e "s/|//g" -e "s/ .*//g" -e "s/^/ '/g" -e "s/$/',/g"
HuffmanTable.requestHuffmanTable = new HuffmanTable([
'111111111111111111110111010',
'111111111111111111110111011',
'111111111111111111110111100',
'111111111111111111110111101',
'111111111111111111110111110',
'111111111111111111110111111',
'111111111111111111111000000',
'111111111111111111111000001',
'111111111111111111111000010',
'111111111111111111111000011',
'111111111111111111111000100',
'111111111111111111111000101',
'111111111111111111111000110',
'111111111111111111111000111',
'111111111111111111111001000',
'111111111111111111111001001',
'111111111111111111111001010',
'111111111111111111111001011',
'111111111111111111111001100',
'111111111111111111111001101',
'111111111111111111111001110',
'111111111111111111111001111',
'111111111111111111111010000',
'111111111111111111111010001',
'111111111111111111111010010',
'111111111111111111111010011',
'111111111111111111111010100',
'111111111111111111111010101',
'111111111111111111111010110',
'111111111111111111111010111',
'111111111111111111111011000',
'111111111111111111111011001',
'11101000',
'111111111100',
'11111111111010',
'111111111111100',
'111111111111101',
'100100',
'1101110',
'111111111111110',
'11111111010',
'11111111011',
'1111111010',
'11111111100',
'11101001',
'100101',
'00100',
'0000',
'00101',
'00110',
'00111',
'100110',
'100111',
'101000',
'101001',
'101010',
'101011',
'101100',
'111101100',
'11101010',
'111111111111111110',
'101101',
'11111111111111100',
'111101101',
'11111111111011',
'1101111',
'11101011',
'11101100',
'11101101',
'11101110',
'1110000',
'111101110',
'111101111',
'111110000',
'111110001',
'1111111011',
'111110010',
'11101111',
'111110011',
'111110100',
'111110101',
'111110110',
'111110111',
'11110000',
'11110001',
'111111000',
'111111001',
'111111010',
'111111011',
'111111100',
'1111111100',
'11111111111100',
'111111111111111111111011010',
'1111111111100',
'11111111111101',
'101110',
'1111111111111111110',
'01000',
'101111',
'01001',
'110000',
'0001',
'110001',
'110010',
'110011',
'01010',
'1110001',
'1110010',
'01011',
'110100',
'01100',
'01101',
'01110',
'11110010',
'01111',
'10000',
'10001',
'110101',
'1110011',
'110110',
'11110011',
'11110100',
'11110101',
'11111111111111101',
'11111111101',
'11111111111111110',
'111111111101',
'111111111111111111111011011',
'111111111111111111111011100',
'111111111111111111111011101',
'111111111111111111111011110',
'111111111111111111111011111',
'111111111111111111111100000',
'111111111111111111111100001',
'111111111111111111111100010',
'111111111111111111111100011',
'111111111111111111111100100',
'111111111111111111111100101',
'111111111111111111111100110',
'111111111111111111111100111',
'111111111111111111111101000',
'111111111111111111111101001',
'111111111111111111111101010',
'111111111111111111111101011',
'111111111111111111111101100',
'111111111111111111111101101',
'111111111111111111111101110',
'111111111111111111111101111',
'111111111111111111111110000',
'111111111111111111111110001',
'111111111111111111111110010',
'111111111111111111111110011',
'111111111111111111111110100',
'111111111111111111111110101',
'111111111111111111111110110',
'111111111111111111111110111',
'111111111111111111111111000',
'111111111111111111111111001',
'111111111111111111111111010',
'111111111111111111111111011',
'111111111111111111111111100',
'111111111111111111111111101',
'111111111111111111111111110',
'111111111111111111111111111',
'11111111111111111110000000',
'11111111111111111110000001',
'11111111111111111110000010',
'11111111111111111110000011',
'11111111111111111110000100',
'11111111111111111110000101',
'11111111111111111110000110',
'11111111111111111110000111',
'11111111111111111110001000',
'11111111111111111110001001',
'11111111111111111110001010',
'11111111111111111110001011',
'11111111111111111110001100',
'11111111111111111110001101',
'11111111111111111110001110',
'11111111111111111110001111',
'11111111111111111110010000',
'11111111111111111110010001',
'11111111111111111110010010',
'11111111111111111110010011',
'11111111111111111110010100',
'11111111111111111110010101',
'11111111111111111110010110',
'11111111111111111110010111',
'11111111111111111110011000',
'11111111111111111110011001',
'11111111111111111110011010',
'11111111111111111110011011',
'11111111111111111110011100',
'11111111111111111110011101',
'11111111111111111110011110',
'11111111111111111110011111',
'11111111111111111110100000',
'11111111111111111110100001',
'11111111111111111110100010',
'11111111111111111110100011',
'11111111111111111110100100',
'11111111111111111110100101',
'11111111111111111110100110',
'11111111111111111110100111',
'11111111111111111110101000',
'11111111111111111110101001',
'11111111111111111110101010',
'11111111111111111110101011',
'11111111111111111110101100',
'11111111111111111110101101',
'11111111111111111110101110',
'11111111111111111110101111',
'11111111111111111110110000',
'11111111111111111110110001',
'11111111111111111110110010',
'11111111111111111110110011',
'11111111111111111110110100',
'11111111111111111110110101',
'11111111111111111110110110',
'11111111111111111110110111',
'11111111111111111110111000',
'11111111111111111110111001',
'11111111111111111110111010',
'11111111111111111110111011',
'11111111111111111110111100',
'11111111111111111110111101',
'11111111111111111110111110',
'11111111111111111110111111',
'11111111111111111111000000',
'11111111111111111111000001',
'11111111111111111111000010',
'11111111111111111111000011',
'11111111111111111111000100',
'11111111111111111111000101',
'11111111111111111111000110',
'11111111111111111111000111',
'11111111111111111111001000',
'11111111111111111111001001',
'11111111111111111111001010',
'11111111111111111111001011',
'11111111111111111111001100',
'11111111111111111111001101',
'11111111111111111111001110',
'11111111111111111111001111',
'11111111111111111111010000',
'11111111111111111111010001',
'11111111111111111111010010',
'11111111111111111111010011',
'11111111111111111111010100',
'11111111111111111111010101',
'11111111111111111111010110',
'11111111111111111111010111',
'11111111111111111111011000',
'11111111111111111111011001',
'11111111111111111111011010',
'11111111111111111111011011',
'11111111111111111111011100'
]);
HuffmanTable.responseHuffmanTable = new HuffmanTable([
'1111111111111111110111100',
'1111111111111111110111101',
'1111111111111111110111110',
'1111111111111111110111111',
'1111111111111111111000000',
'1111111111111111111000001',
'1111111111111111111000010',
'1111111111111111111000011',
'1111111111111111111000100',
'1111111111111111111000101',
'1111111111111111111000110',
'1111111111111111111000111',
'1111111111111111111001000',
'1111111111111111111001001',
'1111111111111111111001010',
'1111111111111111111001011',
'1111111111111111111001100',
'1111111111111111111001101',
'1111111111111111111001110',
'1111111111111111111001111',
'1111111111111111111010000',
'1111111111111111111010001',
'1111111111111111111010010',
'1111111111111111111010011',
'1111111111111111111010100',
'1111111111111111111010101',
'1111111111111111111010110',
'1111111111111111111010111',
'1111111111111111111011000',
'1111111111111111111011001',
'1111111111111111111011010',
'1111111111111111111011011',
'0000',
'111111111010',
'1101010',
'1111111111010',
'11111111111100',
'111101100',
'1111111000',
'1111111111011',
'111101101',
'111101110',
'111111111011',
'11111111010',
'100010',
'100011',
'100100',
'1101011',
'0001',
'0010',
'0011',
'01000',
'01001',
'01010',
'100101',
'100110',
'01011',
'01100',
'01101',
'111101111',
'1111111111111010',
'1101100',
'1111111111100',
'111111111100',
'1111111111111011',
'1101101',
'11101010',
'11101011',
'11101100',
'11101101',
'11101110',
'100111',
'111110000',
'11101111',
'11110000',
'1111111001',
'111110001',
'101000',
'11110001',
'11110010',
'111110010',
'1111111010',
'111110011',
'101001',
'01110',
'111110100',
'111110101',
'11110011',
'1111111011',
'111110110',
'1111111100',
'11111111011',
'1111111111101',
'11111111100',
'111111111111100',
'111110111',
'11111111111111110',
'01111',
'1101110',
'101010',
'101011',
'10000',
'1101111',
'1110000',
'1110001',
'101100',
'111111000',
'111111001',
'1110010',
'101101',
'101110',
'101111',
'110000',
'111111010',
'110001',
'110010',
'110011',
'110100',
'1110011',
'11110100',
'1110100',
'11110101',
'111111011',
'1111111111111100',
'11111111111101',
'1111111111111101',
'1111111111111110',
'1111111111111111111011100',
'1111111111111111111011101',
'1111111111111111111011110',
'1111111111111111111011111',
'1111111111111111111100000',
'1111111111111111111100001',
'1111111111111111111100010',
'1111111111111111111100011',
'1111111111111111111100100',
'1111111111111111111100101',
'1111111111111111111100110',
'1111111111111111111100111',
'1111111111111111111101000',
'1111111111111111111101001',
'1111111111111111111101010',
'1111111111111111111101011',
'1111111111111111111101100',
'1111111111111111111101101',
'1111111111111111111101110',
'1111111111111111111101111',
'1111111111111111111110000',
'1111111111111111111110001',
'1111111111111111111110010',
'1111111111111111111110011',
'1111111111111111111110100',
'1111111111111111111110101',
'1111111111111111111110110',
'1111111111111111111110111',
'1111111111111111111111000',
'1111111111111111111111001',
'1111111111111111111111010',
'1111111111111111111111011',
'1111111111111111111111100',
'1111111111111111111111101',
'1111111111111111111111110',
'1111111111111111111111111',
'111111111111111110000000',
'111111111111111110000001',
'111111111111111110000010',
'111111111111111110000011',
'111111111111111110000100',
'111111111111111110000101',
'111111111111111110000110',
'111111111111111110000111',
'111111111111111110001000',
'111111111111111110001001',
'111111111111111110001010',
'111111111111111110001011',
'111111111111111110001100',
'111111111111111110001101',
'111111111111111110001110',
'111111111111111110001111',
'111111111111111110010000',
'111111111111111110010001',
'111111111111111110010010',
'111111111111111110010011',
'111111111111111110010100',
'111111111111111110010101',
'111111111111111110010110',
'111111111111111110010111',
'111111111111111110011000',
'111111111111111110011001',
'111111111111111110011010',
'111111111111111110011011',
'111111111111111110011100',
'111111111111111110011101',
'111111111111111110011110',
'111111111111111110011111',
'111111111111111110100000',
'111111111111111110100001',
'111111111111111110100010',
'111111111111111110100011',
'111111111111111110100100',
'111111111111111110100101',
'111111111111111110100110',
'111111111111111110100111',
'111111111111111110101000',
'111111111111111110101001',
'111111111111111110101010',
'111111111111111110101011',
'111111111111111110101100',
'111111111111111110101101',
'111111111111111110101110',
'111111111111111110101111',
'111111111111111110110000',
'111111111111111110110001',
'111111111111111110110010',
'111111111111111110110011',
'111111111111111110110100',
'111111111111111110110101',
'111111111111111110110110',
'111111111111111110110111',
'111111111111111110111000',
'111111111111111110111001',
'111111111111111110111010',
'111111111111111110111011',
'111111111111111110111100',
'111111111111111110111101',
'111111111111111110111110',
'111111111111111110111111',
'111111111111111111000000',
'111111111111111111000001',
'111111111111111111000010',
'111111111111111111000011',
'111111111111111111000100',
'111111111111111111000101',
'111111111111111111000110',
'111111111111111111000111',
'111111111111111111001000',
'111111111111111111001001',
'111111111111111111001010',
'111111111111111111001011',
'111111111111111111001100',
'111111111111111111001101',
'111111111111111111001110',
'111111111111111111001111',
'111111111111111111010000',
'111111111111111111010001',
'111111111111111111010010',
'111111111111111111010011',
'111111111111111111010100',
'111111111111111111010101',
'111111111111111111010110',
'111111111111111111010111',
'111111111111111111011000',
'111111111111111111011001',
'111111111111111111011010',
'111111111111111111011011',
'111111111111111111011100',
'111111111111111111011101'
]);
// ### String literal representation ###
//
// Literal **strings** can represent header names or header values. They are encoded in two parts:
// Literal **strings** can represent header names or header values. There's two variant of the
// string encoding:
//
// 1. The string length, defined as the number of bytes needed to store its UTF-8 representation,
// is represented as an integer with a zero bits prefix. If the string length is strictly less
// than 128, it is represented as one byte.
// 2. The string value represented as a list of UTF-8 characters.
// String literal with Huffman encoding:
//
// 0 1 2 3 4 5 6 7
// +---+---+---+---+---+---+---+---+
// | 1 | Value Length Prefix (7) |
// +---+---+---+---+---+---+---+---+
// | Value Length (0-N bytes) |
// +---+---+---+---+---+---+---+---+
// ...
// +---+---+---+---+---+---+---+---+
// | Huffman Encoded Data |Padding|
// +---+---+---+---+---+---+---+---+
//
// String literal without Huffman encoding:
//
// 0 1 2 3 4 5 6 7
// +---+---+---+---+---+---+---+---+
// | 0 | Value Length Prefix (7) |
// +---+---+---+---+---+---+---+---+
// | Value Length (0-N bytes) |
// +---+---+---+---+---+---+---+---+
// ...
// +---+---+---+---+---+---+---+---+
// | Field Bytes Without Encoding |
// +---+---+---+---+---+---+---+---+
HeaderSetCompressor.string = function writeString(str) {
var encodedString = new Buffer(str, 'utf8');
var encodedLength = HeaderSetCompressor.integer(encodedString.length, 0);
return encodedLength.concat(encodedString);
HeaderSetCompressor.string = function writeString(str, huffmanTable) {
str = new Buffer(str, 'utf8');
var huffman = huffmanTable.encode(str);
if (huffman.length < str.length) {
var length = HeaderSetCompressor.integer(huffman.length, 7)
length[0][0] |= 128;
return length.concat(huffman);
}
else {
length = HeaderSetCompressor.integer(str.length, 7)
return length.concat(str);
}
};
HeaderSetDecompressor.string = function readString(buffer) {
var length = HeaderSetDecompressor.integer(buffer, 0);
var str = buffer.toString('utf8', buffer.cursor, buffer.cursor + length);
HeaderSetDecompressor.string = function readString(buffer, huffmanTable) {
var huffman = buffer[buffer.cursor] & 128;
var length = HeaderSetDecompressor.integer(buffer, 7);
var encoded = buffer.slice(buffer.cursor, buffer.cursor + length);
buffer.cursor += length;
return str;
return (huffman ? huffmanTable.decode(encoded) : encoded).toString('utf8');
};

@@ -616,3 +1296,3 @@

// The JavaScript object representation is described near the
// `HeaderTable.prototype.execute()` method definition.
// `HeaderSetDecompressor.prototype._execute()` method definition.
//

@@ -627,14 +1307,46 @@ // **All binary header representations** start with a prefix signaling the representation type and

//
// 0 1 2 3 4 5 6 7
// +---+---+---+---+---+---+---+---+
// | 0 | 1 | 1 | Index (5+) | Literal w/o Indexing
// +---+---+---+-------------------+
// | 0 | 1 | Index (6+) |
// +---+---+---+-------------------+ Literal w/o Indexing
// | Value Length (8+) |
// +-------------------------------+ w/ Indexed Name
// | Value String (Length octets) |
// +-------------------------------+
//
// 0 1 2 3 4 5 6 7
// +---+---+---+---+---+---+---+---+
// | 0 | 1 | 0 | Index (5+) | Literal w/ Incremental Indexing
// | 0 | 1 | 0 |
// +---+---+---+-------------------+
// | Name Length (8+) |
// +-------------------------------+ Literal w/o Indexing
// | Name String (Length octets) |
// +-------------------------------+ w/ New Name
// | Value Length (8+) |
// +-------------------------------+
// | Value String (Length octets) |
// +-------------------------------+
//
// 0 1 2 3 4 5 6 7
// +---+---+---+---+---+---+---+---+
// | 0 | 0 | Index (6+) | Literal w/ Substitution Indexing
// +---+---+-----------------------+
// | 0 | 0 | Index (6+) |
// +---+---+---+-------------------+ Literal w/ Incremental Indexing
// | Value Length (8+) |
// +-------------------------------+ w/ Indexed Name
// | Value String (Length octets) |
// +-------------------------------+
//
// 0 1 2 3 4 5 6 7
// +---+---+---+---+---+---+---+---+
// | 0 | 0 | 0 |
// +---+---+---+-------------------+
// | Name Length (8+) |
// +-------------------------------+ Literal w/ Incremental Indexing
// | Name String (Length octets) |
// +-------------------------------+ w/ New Name
// | Value Length (8+) |
// +-------------------------------+
// | Value String (Length octets) |
// +-------------------------------+
//
// The **Indexed Representation** consists of the 1-bit prefix and the Index that is represented as

@@ -647,5 +1359,2 @@ // a 7-bit prefix coded integer and nothing else.

//
// When using **Substitution Indexing**, a new index comes next represented as a 0-bit prefix
// integer, specifying the record in the Header Table that needs to be replaced.
//
// For **all literal representations**, the specification of the header value comes next. It is

@@ -656,8 +1365,7 @@ // always represented as a string.

indexed : { prefix: 7, pattern: 0x80 },
literal : { prefix: 5, pattern: 0x60 },
literalIncremental : { prefix: 5, pattern: 0x40 },
literalSubstitution : { prefix: 6, pattern: 0x00 }
literal : { prefix: 6, pattern: 0x40 },
literalIncremental : { prefix: 6, pattern: 0x00 }
};
HeaderSetCompressor.header = function writeHeader(header) {
HeaderSetCompressor.header = function writeHeader(header, huffmanTable) {
var representation, buffers = [];

@@ -667,8 +1375,6 @@

representation = representations.indexed;
} else if (header.index === -1) {
representation = representations.literal;
} else if (header.index === Infinity) {
} else if (header.index) {
representation = representations.literalIncremental;
} else {
representation = representations.literalSubstitution;
representation = representations.literal;
}

@@ -684,10 +1390,6 @@

buffers.push(HeaderSetCompressor.integer(0, representation.prefix));
buffers.push(HeaderSetCompressor.string(header.name));
buffers.push(HeaderSetCompressor.string(header.name, huffmanTable));
}
if (representation === representations.literalSubstitution) {
buffers.push(HeaderSetCompressor.integer(header.index, 0));
}
buffers.push(HeaderSetCompressor.string(header.value));
buffers.push(HeaderSetCompressor.string(header.value, huffmanTable));
}

@@ -700,3 +1402,3 @@

HeaderSetDecompressor.header = function readHeader(buffer) {
HeaderSetDecompressor.header = function readHeader(buffer, huffmanTable) {
var representation, header = {};

@@ -708,9 +1410,5 @@

} else if (firstByte & 0x40) {
if (firstByte & 0x20) {
representation = representations.literal;
} else {
representation = representations.literalIncremental;
}
representation = representations.literal;
} else {
representation = representations.literalSubstitution;
representation = representations.literalIncremental;
}

@@ -720,3 +1418,3 @@

header.value = header.name = HeaderSetDecompressor.integer(buffer, representation.prefix);
header.index = -1;
header.index = false;

@@ -726,14 +1424,8 @@ } else {

if (header.name === -1) {
header.name = HeaderSetDecompressor.string(buffer);
header.name = HeaderSetDecompressor.string(buffer, huffmanTable);
}
if (representation === representations.literalSubstitution) {
header.index = HeaderSetDecompressor.integer(buffer, 0);
} else if (representation === representations.literalIncremental) {
header.index = Infinity;
} else {
header.index = -1;
}
header.value = HeaderSetDecompressor.string(buffer, huffmanTable);
header.value = HeaderSetDecompressor.string(buffer);
header.index = (representation === representations.literalIncremental);
}

@@ -779,7 +1471,12 @@

assert((type === 'REQUEST') || (type === 'RESPONSE'));
var initialTable = (type === 'REQUEST') ? HeaderTable.initialRequestTable
: HeaderTable.initialResponseTable;
this._table = new HeaderTable(this._log, initialTable);
this._huffmanTable = (type === 'REQUEST') ? HuffmanTable.requestHuffmanTable
: HuffmanTable.responseHuffmanTable;
this._table = new HeaderTable(this._log);
}
// Changing the header table size
Compressor.prototype.setTableSizeLimit = function setTableSizeLimit(size) {
this._table.setSizeLimit(size);
};
// `compress` takes a header set, and compresses it using a new `HeaderSetCompressor` stream

@@ -789,3 +1486,3 @@ // instance. This means that from now on, the advantages of streaming header encoding are lost,

Compressor.prototype.compress = function compress(headers) {
var compressor = new HeaderSetCompressor(this._log, this._table);
var compressor = new HeaderSetCompressor(this._log, this._table, this._huffmanTable);
for (var name in headers) {

@@ -840,5 +1537,2 @@ var value = headers[name];

}
if (chunkFrame.type !== 'PUSH_PROMISE') {
chunkFrame.flags.END_STREAM = last && frame.flags.END_STREAM;
}
chunkFrame.data = chunks[i];

@@ -874,5 +1568,5 @@

assert((type === 'REQUEST') || (type === 'RESPONSE'));
var initialTable = (type === 'REQUEST') ? HeaderTable.initialRequestTable
: HeaderTable.initialResponseTable;
this._table = new HeaderTable(this._log, initialTable);
this._huffmanTable = (type === 'REQUEST') ? HuffmanTable.requestHuffmanTable
: HuffmanTable.responseHuffmanTable;
this._table = new HeaderTable(this._log);

@@ -883,2 +1577,7 @@ this._inProgress = false;

// Changing the header table size
Decompressor.prototype.setTableSizeLimit = function setTableSizeLimit(size) {
this._table.setSizeLimit(size);
};
// `decompress` takes a full header block, and decompresses it using a new `HeaderSetDecompressor`

@@ -888,3 +1587,3 @@ // stream instance. This means that from now on, the advantages of streaming header decoding are

Decompressor.prototype.decompress = function decompress(block) {
var decompressor = new HeaderSetDecompressor(this._log, this._table);
var decompressor = new HeaderSetDecompressor(this._log, this._table, this._huffmanTable);
decompressor.end(block);

@@ -891,0 +1590,0 @@

54

lib/connection.js

@@ -28,4 +28,4 @@ var assert = require('assert');

//
// * **set(settings)**: change the value of one or more settings according to the key-value pairs
// of `settings`
// * **set(settings, callback)**: change the value of one or more settings according to the
// key-value pairs of `settings`. The callback is called after the peer acknowledged the changes.
//

@@ -386,2 +386,5 @@ // * **ping([callback])**: send a ping and call callback when the answer arrives

Connection.prototype._initializeSettingsManagement = function _initializeSettingsManagement(settings) {
// * Setting up the callback queue for setting acknowledgements
this._settingsAckCallbacks = [];
// * Sending the initial settings.

@@ -399,4 +402,3 @@ this._log.debug({ settings: settings },

if ((frame.stream === 0) && (frame.type === 'SETTINGS')) {
this._log.debug({ settings: frame.settings },
'Receiving the first SETTINGS frame as part of the connection header.');
this._log.debug('Receiving the first SETTINGS frame as part of the connection header.');
} else {

@@ -410,12 +412,42 @@ this._log.fatal({ frame: frame }, 'Invalid connection header: first frame is not SETTINGS.');

Connection.prototype._receiveSettings = function _receiveSettings(frame) {
for (var name in frame.settings) {
this.emit('RECEIVING_' + name, frame.settings[name]);
// * If it's an ACK, call the appropriate callback
if (frame.flags.ACK) {
var callback = this._settingsAckCallbacks.shift();
if (callback) {
callback();
}
}
// * If it's a setting change request, then send an ACK and change the appropriate settings
else {
if (!this._closed) {
this.push({
type: 'SETTINGS',
flags: { ACK: true },
stream: 0,
settings: {}
});
}
for (var name in frame.settings) {
this.emit('RECEIVING_' + name, frame.settings[name]);
}
}
};
// Changing one or more settings value and sending out a SETTINGS frame
Connection.prototype.set = function set(settings) {
Connection.prototype.set = function set(settings, callback) {
// * Calling the callback and emitting event when the change is acknowledges
callback = callback || function noop() {};
var self = this;
this._settingsAckCallbacks.push(function() {
for (var name in settings) {
self.emit('ACKNOWLEDGED_' + name, settings[name]);
}
callback();
});
// * Sending out the SETTINGS frame
this.push({
type: 'SETTINGS',
flags: {},
flags: { ACK: false },
stream: 0,

@@ -467,3 +499,3 @@ settings: settings

flags: {
PONG: false
ACK: false
},

@@ -477,3 +509,3 @@ stream: 0,

Connection.prototype._receivePing = function _receivePing(frame) {
if (frame.flags.PONG) {
if (frame.flags.ACK) {
var id = frame.data.toString('hex');

@@ -496,3 +528,3 @@ if (id in this._pings) {

flags: {
PONG: true
ACK: true
},

@@ -499,0 +531,0 @@ stream: 0,

@@ -194,2 +194,7 @@ var assert = require('assert');

pipeAndFilter(this._decompressor, this._connection, filters.afterDecompression);
this._connection.on('ACKNOWLEDGED_SETTINGS_HEADER_TABLE_SIZE',
this._decompressor.setTableSizeLimit.bind(this._decompressor))
this._connection.on('RECEIVING_SETTINGS_HEADER_TABLE_SIZE',
this._compressor.setTableSizeLimit.bind(this._compressor))
};

@@ -196,0 +201,0 @@

@@ -116,3 +116,3 @@ // The framer consists of two [Transform Stream][1] subclasses that operate in [object mode][2]:

} else {
this.emit('error', 'FRAME_TOO_LARGE');
this.emit('error', 'FRAME_SIZE_ERROR');
return;

@@ -159,4 +159,4 @@ }

// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Length (16) | Type (8) | Flags (8) |
// +-+-------------+---------------+-------------------------------+
// | R | Length (14) | Type (8) | Flags (8) |
// +-+-+---------------------------+---------------+---------------+
// |R| Stream Identifier (31) |

@@ -169,4 +169,8 @@ // +-+-------------------------------------------------------------+

//
// * R:
// A reserved 2-bit field. The semantics of these bits are undefined and the bits MUST remain
// unset (0) when sending and MUST be ignored when receiving.
//
// * Length:
// The length of the frame data expressed as an unsigned 16-bit integer. The 8 bytes of the frame
// The length of the frame data expressed as an unsigned 14-bit integer. The 8 bytes of the frame
// header are not included in this value.

@@ -189,4 +193,4 @@ //

// * Stream Identifier:
// A 31-bit stream identifier (see Section 3.4.1). A value 0 is reserved for frames that are
// associated with the connection as a whole as opposed to an individual stream.
// A 31-bit stream identifier. The value 0 is reserved for frames that are associated with the
// connection as a whole as opposed to an individual stream.
//

@@ -196,3 +200,3 @@ // The structure and content of the remaining frame data is dependent entirely on the frame type.

var COMMON_HEADER_SIZE = 8;
var MAX_PAYLOAD_SIZE = 65535;
var MAX_PAYLOAD_SIZE = 16383;

@@ -416,7 +420,10 @@ var frameTypes = [];

//
// The SETTINGS frame does not define any flags.
// The SETTINGS frame defines the following flag:
// * ACK (0x1):
// Bit 1 being set indicates that this frame acknowledges receipt and application of the peer's
// SETTINGS frame.
frameTypes[0x4] = 'SETTINGS';
frameFlags.SETTINGS = [];
frameFlags.SETTINGS = ['ACK'];

@@ -484,2 +491,13 @@ typeSpecificAttributes.SETTINGS = ['settings'];

// * SETTINGS_HEADER_TABLE_SIZE (1):
// Allows the sender to inform the remote endpoint of the size of the header compression table
// used to decode header blocks.
definedSettings[1] = { name: 'SETTINGS_HEADER_TABLE_SIZE', flag: false };
// * SETTINGS_ENABLE_PUSH (2):
// This setting can be use to disable server push. An endpoint MUST NOT send a PUSH_PROMISE frame
// if it receives this setting set to a value of 0. The default value is 1, which indicates that
// push is permitted.
definedSettings[2] = { name: 'SETTINGS_ENABLE_PUSH', flag: true };
// * SETTINGS_MAX_CONCURRENT_STREAMS (4):

@@ -507,3 +525,3 @@ // indicates the maximum number of concurrent streams that the sender will allow.

//
// * END_PUSH_PROMISE (0x1):
// * END_PUSH_PROMISE (0x4):
// The END_PUSH_PROMISE bit indicates that this frame contains the entire payload necessary to

@@ -514,3 +532,3 @@ // provide a complete set of headers.

frameFlags.PUSH_PROMISE = ['END_PUSH_PROMISE'];
frameFlags.PUSH_PROMISE = ['RESERVED1', 'RESERVED2', 'END_PUSH_PROMISE'];

@@ -555,8 +573,8 @@ typeSpecificAttributes.PUSH_PROMISE = ['promised_stream', 'headers', 'data'];

//
// * PONG (0x2):
// Bit 2 being set indicates that this PING frame is a PING response.
// * ACK (0x1):
// Bit 1 being set indicates that this PING frame is a PING response.
frameTypes[0x6] = 'PING';
frameFlags.PING = ['PONG'];
frameFlags.PING = ['ACK'];

@@ -662,9 +680,4 @@ typeSpecificAttributes.PING = ['data'];

//
// The CONTINUATION frame defines the following flags:
// The CONTINUATION frame defines the following flag:
//
// * END_STREAM (0x1):
// Bit 1 being set indicates that this frame is the last that the endpoint will send for the
// identified stream.
// * RESERVED (0x2):
// Bit 2 is reserved for future use.
// * END_HEADERS (0x4):

@@ -676,3 +689,3 @@ // The END_HEADERS bit indicates that this frame ends the sequence of header block fragments

frameFlags.CONTINUATION = ['END_STREAM', 'RESERVED', 'END_HEADERS'];
frameFlags.CONTINUATION = ['RESERVED1', 'RESERVED2', 'END_HEADERS'];

@@ -697,9 +710,11 @@ typeSpecificAttributes.CONTINUATION = ['headers', 'data'];

'FLOW_CONTROL_ERROR',
,
'SETTINGS_TIMEOUT',
'STREAM_CLOSED',
'FRAME_TOO_LARGE',
'FRAME_SIZE_ERROR',
'REFUSED_STREAM',
'CANCEL',
'COMPRESSION_ERROR'
'COMPRESSION_ERROR',
'CONNECT_ERROR'
];
errorCodes[420] = 'ENHANCE_YOUR_CALM';

@@ -706,0 +721,0 @@ // Logging

{
"name": "http2-protocol",
"version": "0.6.0",
"version": "0.7.0",
"description": "A JavaScript implementation of the HTTP/2 framing layer",

@@ -5,0 +5,0 @@ "main": "lib/index.js",

@@ -1,6 +0,6 @@

node-http2
==========
node-http2-protocol
===================
An HTTP/2 ([draft-ietf-httpbis-http2-06](http://tools.ietf.org/html/draft-ietf-httpbis-http2-06))
client and server implementation for node.js.
An HTTP/2 ([draft-ietf-httpbis-http2-07](http://tools.ietf.org/html/draft-ietf-httpbis-http2-07))
framing layer implementaion for node.js.

@@ -11,75 +11,11 @@ Installation

```
npm install http2
npm install http2-protocol
```
API
---
The API is very similar to the [standard node.js HTTPS API](http://nodejs.org/api/https.html). The
goal is the perfect API compatibility, with additional HTTP2 related extensions (like server push).
Detailed API documentation is primarily maintained in the `lib/http.js` file and is [available in
the wiki](https://github.com/molnarg/node-http2/wiki/Public-API) as well.
Examples
--------
### Using as a server ###
API
---
```javascript
var options = {
key: fs.readFileSync('./example/localhost.key'),
cert: fs.readFileSync('./example/localhost.crt')
};
require('http2').createServer(options, function(request, response) {
response.end('Hello world!');
}).listen(8080);
```
### Using as a client ###
```javascript
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
require('http2').get('https://localhost:8080/', function(response) {
response.pipe(process.stdout);
});
```
### Simple static file server ###
An simple static file server serving up content from its own directory is available in the `example`
directory. Running the server:
```bash
$ node ./example/server.js
```
### Simple command line client ###
An example client is also available. Downloading the server's own source code from the server:
```bash
$ node ./example/client.js 'https://localhost:8080/server.js' >/tmp/server.js
```
### Server push ###
For a server push example, see the source code of the example
[server](https://github.com/molnarg/node-http2/blob/master/example/server.js) and
[client](https://github.com/molnarg/node-http2/blob/master/example/client.js).
Status
------
* ALPN is not yet supported in node.js (see
[this issue](https://github.com/joyent/node/issues/5945)). For ALPN support, you will have to use
[Shigeki Ohtsu's node.js fork](https://github.com/shigeki/node/tree/alpn_support) until this code
gets merged upstream.
* Upgrade mechanism to start HTTP/2 over unencrypted channel is not implemented yet
(issue [#4](https://github.com/molnarg/node-http2/issues/4))
* Other minor features found in
[this list](https://github.com/molnarg/node-http2/issues?labels=feature) are not implemented yet
Development

@@ -117,35 +53,16 @@ -----------

To generate a code coverage report, run `npm test --coverage` (which runs very slowly, be patient).
Code coverage summary as of version 1.0.1:
To generate a code coverage report, run `npm test --coverage` (it may be slow, be patient).
Code coverage summary as of version 0.6.0:
```
Statements : 93.26% ( 1563/1676 )
Branches : 84.85% ( 605/713 )
Functions : 94.81% ( 201/212 )
Lines : 93.23% ( 1557/1670 )
Statements : 92.39% ( 1165/1261 )
Branches : 86.57% ( 477/551 )
Functions : 91.22% ( 135/148 )
Lines : 92.35% ( 1159/1255 )
```
There's a hosted version of the detailed (line-by-line) coverage report
[here](http://molnarg.github.io/node-http2/coverage/lcov-report/lib/).
[here](http://molnarg.github.io/node-http2-protocol/coverage/lcov-report/lib/).
### Logging ###
Logging is turned off by default. You can turn it on by passing a bunyan logger as `log` option when
creating a server or agent.
When using the example server or client, it's very easy to turn logging on: set the `HTTP2_LOG`
environment variable to `fatal`, `error`, `warn`, `info`, `debug` or `trace` (the logging level).
To log every single incoming and outgoing data chunk, use `HTTP2_LOG_DATA=1` besides
`HTTP2_LOG=trace`. Log output goes to the standard error output. If the standard error is redirected
into a file, then the log output is in bunyan's JSON format for easier post-mortem analysis.
Running the example server and client with `info` level logging output:
```bash
$ HTTP2_LOG=info node ./example/server.js
```
```bash
$ HTTP2_LOG=info node ./example/client.js 'http://localhost:8080/server.js' >/dev/null
```
Contributors

@@ -152,0 +69,0 @@ ------------

@@ -6,2 +6,3 @@ var expect = require('chai').expect;

var HeaderTable = compressor.HeaderTable;
var HuffmanTable = compressor.HuffmanTable;
var HeaderSetCompressor = compressor.HeaderSetCompressor;

@@ -31,4 +32,4 @@ var HeaderSetDecompressor = compressor.HeaderSetDecompressor;

var test_strings = [{
string: 'abcdefghij',
buffer: new Buffer('0A6162636465666768696A', 'hex')
string: 'www.foo.com',
buffer: new Buffer('88db6d898b5a44b74f', 'hex')
}, {

@@ -39,58 +40,161 @@ string: 'éáűőúöüó€',

test_huffman_request = {
'GET': 'f77778ff',
'http': 'ce3177',
'/': '0f',
'www.foo.com': 'db6d898b5a44b74f',
'https': 'ce31743f',
'www.bar.com': 'db6d897a1e44b74f',
'no-cache': '63654a1398ff',
'/custom-path.css': '04eb08b7495c88e644c21f',
'custom-key': '4eb08b749790fa7f',
'custom-value': '4eb08b74979a17a8ff'
};
test_huffman_response = {
'302': '409f',
'private': 'c31b39bf387f',
'Mon, 21 OCt 2013 20:13:21 GMT': 'a2fba20320f2ebcc0c490062d2434c827a1d',
': https://www.bar.com': '6871cf3c326ebd7e9e9e926e7e32557dbf',
'200': '311f',
'Mon, 21 OCt 2013 20:13:22 GMT': 'a2fba20320f2ebcc0c490062d2434cc27a1d',
'https://www.bar.com': 'e39e7864dd7afd3d3d24dcfc64aafb7f',
'gzip': 'e1fbb30f',
'foo=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
AAAAAAAAAAAAAAAAAAAAAAAAAALASDJKHQKBZXOQWEOPIUAXQWEOIUAXLJKHQWOEIUAL\
QWEOIUAXLQEUAXLLKJASDQWEOUIAXN1234LASDJKHQKBZXOQWEOPIUAXQWEOIUAXLJKH\
QWOEIUALQWEOIUAXLQEUAXLLKJASDQWEOUIAXN1234LASDJKHQKBZXOQWEOPIUAXQWEO\
IUAXLJKHQWOEIUALQWEOIUAXLQEUAXLLKJASDQWEOUIAXN1234LASDJKHQKBZXOQWEOP\
IUAXQWEOIUAXLJKHQWOEIUALQWEOIUAXLQEUAXLLKJASDQWEOUIAXN1234ZZZZZZZZZZ\
ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ1234 m\
ax-age=3600; version=1': 'df7dfb36eddbb76eddbb76eddbb76eddbb76eddbb76eddbb76\
eddbb76eddbb76eddbb76eddbb76eddbb76eddbb76eddbb76eddbb76eddbb76eddbb\
76eddbb76eddbb7e3b69ecf0fe7e1fd7f3d5fe7f7e5fd79f6f97cbbfe9b7fbfebcfb\
7cbbfe9b7fbf8f87f3f0febcfcbb7bfe9b7e3fd79f6f977fd36ff7f1febb7e9b7fbf\
8fc7f9f0db4f67f5e7dbe5f4efdbfdf891a13f1db4f6787f3f0febf9eaff3fbf2feb\
cfb7cbe5dff4dbfdff5e7dbe5dff4dbfdfc7c3f9f87f5e7e5dbdff4dbf1febcfb7cb\
bfe9b7fbf8ff5dbf4dbfdfc7e3fcf86da7b3faf3edf2fa77edfefc48d09f8eda7b3c\
3f9f87f5fcf57f9fdf97f5e7dbe5f2effa6dfeffaf3edf2effa6dfefe3e1fcfc3faf\
3f2edeffa6df8ff5e7dbe5dff4dbfdfc7faedfa6dfefe3f1fe7c36d3d9fd79f6f97d\
3bf6ff7e24684fc76d3d9e1fcfc3fafe7abfcfefcbfaf3edf2f977fd36ff7fd79f6f\
977fd36ff7f1f0fe7e1fd79f976f7fd36fc7faf3edf2effa6dfefe3fd76fd36ff7f1\
f8ff3e1b69ecfebcfb7cbe9dfb7fbf123427fcff3fcff3fcff3fcff3fcff3fcff3fc\
ff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3\
fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcf\
f3fcff3fcff08d090b5fd237f086c44a23ef0e70c72b2fbb617f',
'foo=ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ\
ZZZZZZZZZZZZZZZZZZZZZZZZZZLASDJKHQKBZXOQWEOPIUAXQWEOIUAXLJKHQWOEIUAL\
QWEOIUAXLQEUAXLLKJASDQWEOUIAXN1234LASDJKHQKBZXOQWEOPIUAXQWEOIUAXLJKH\
QWOEIUALQWEOIUAXLQEUAXLLKJASDQWEOUIAXN1234LASDJKHQKBZXOQWEOPIUAXQWEO\
IUAXLJKHQWOEIUALQWEOIUAXLQEUAXLLKJASDQWEOUIAXN1234LASDJKHQKBZXOQWEOP\
IUAXQWEOIUAXLJKHQWOEIUALQWEOIUAXLQEUAXLLKJASDQWEOUIAXN1234AAAAAAAAAA\
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1234 m\
ax-age=3600; version=1': 'df7dfb3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcf\
f3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3f\
cff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff\
3e3b69ecf0fe7e1fd7f3d5fe7f7e5fd79f6f97cbbfe9b7fbfebcfb7cbbfe9b7fbf8f\
87f3f0febcfcbb7bfe9b7e3fd79f6f977fd36ff7f1febb7e9b7fbf8fc7f9f0db4f67\
f5e7dbe5f4efdbfdf891a13f1db4f6787f3f0febf9eaff3fbf2febcfb7cbe5dff4db\
fdff5e7dbe5dff4dbfdfc7c3f9f87f5e7e5dbdff4dbf1febcfb7cbbfe9b7fbf8ff5d\
bf4dbfdfc7e3fcf86da7b3faf3edf2fa77edfefc48d09f8eda7b3c3f9f87f5fcf57f\
9fdf97f5e7dbe5f2effa6dfeffaf3edf2effa6dfefe3e1fcfc3faf3f2edeffa6df8f\
f5e7dbe5dff4dbfdfc7faedfa6dfefe3f1fe7c36d3d9fd79f6f97d3bf6ff7e24684f\
c76d3d9e1fcfc3fafe7abfcfefcbfaf3edf2f977fd36ff7fd79f6f977fd36ff7f1f0\
fe7e1fd79f976f7fd36fc7faf3edf2effa6dfefe3fd76fd36ff7f1f8ff3e1b69ecfe\
bcfb7cbe9dfb7fbf1234276eddbb76eddbb76eddbb76eddbb76eddbb76eddbb76edd\
bb76eddbb76eddbb76eddbb76eddbb76eddbb76eddbb76eddbb76eddbb76eddbb76e\
ddbb76eddbb48d090b5fd237f086c44a23ef0e70c72b2fbb617f'
};
var test_headers = [{
header: {
name: 3,
value: '/my-example/index.html',
index: Infinity
name: 1,
value: 'GET',
index: true
},
buffer: new Buffer('44' + '162F6D792D6578616D706C652F696E6465782E68746D6C', 'hex')
buffer: new Buffer('02' + '03474554', 'hex')
}, {
header: {
name: 11,
value: 'my-user-agent',
index: Infinity
name: 6,
value: 'http',
index: true
},
buffer: new Buffer('4C' + '0D6D792D757365722D6167656E74', 'hex')
buffer: new Buffer('07' + '83ce3177', 'hex')
}, {
header: {
name: 'x-my-header',
value: 'first',
index: Infinity
name: 5,
value: '/',
index: true
},
buffer: new Buffer('40' + '0B782D6D792D686561646572' + '056669727374', 'hex')
buffer: new Buffer('06' + '012f', 'hex')
}, {
header: {
name: 30,
value: 30,
index: -1
name: 3,
value: 'www.foo.com',
index: true
},
buffer: new Buffer('9e', 'hex')
buffer: new Buffer('04' + '88db6d898b5a44b74f', 'hex')
}, {
header: {
name: 32,
value: 32,
index: -1
name: 2,
value: 'https',
index: true
},
buffer: new Buffer('a0', 'hex')
buffer: new Buffer('03' + '84ce31743f', 'hex')
}, {
header: {
name: 1,
value: 'www.bar.com',
index: true
},
buffer: new Buffer('02' + '88db6d897a1e44b74f', 'hex')
}, {
header: {
name: 28,
value: 'no-cache',
index: true
},
buffer: new Buffer('1d' + '8663654a1398ff', 'hex')
}, {
header: {
name: 3,
value: '/my-example/resources/script.js',
index: 30
value: 3,
index: false
},
buffer: new Buffer('041e' + '1F2F6D792D6578616D706C652F7265736F75726365732F7363726970742E6A73', 'hex')
buffer: new Buffer('83', 'hex')
}, {
header: {
name: 32,
value: 'second',
index: Infinity
name: 5,
value: 5,
index: false
},
buffer: new Buffer('5F02' + '067365636F6E64', 'hex')
buffer: new Buffer('85', 'hex')
}, {
header: {
name: 32,
value: 'third',
index: -1
name: 4,
value: '/custom-path.css',
index: true
},
buffer: new Buffer('7F02' + '057468697264', 'hex')
buffer: new Buffer('05' + '8b04eb08b7495c88e644c21f', 'hex')
}, {
header: {
name: 'custom-key',
value: 'custom-value',
index: true
},
buffer: new Buffer('00' + '884eb08b749790fa7f' + '894eb08b74979a17a8ff', 'hex')
}, {
header: {
name: 2,
value: 2,
index: false
},
buffer: new Buffer('82', 'hex')
}, {
header: {
name: 6,
value: 6,
index: false
},
buffer: new Buffer('86', 'hex')
}];

@@ -100,23 +204,37 @@

headers: {
':path': '/my-example/index.html',
'user-agent': 'my-user-agent',
'x-my-header': 'first'
':method': 'GET',
':scheme': 'http',
':path': '/',
':authority': 'www.foo.com'
},
buffer: util.concat(test_headers.slice(0, 3).map(function(test) { return test.buffer; }))
buffer: util.concat(test_headers.slice(0, 4).map(function(test) { return test.buffer; }))
}, {
headers: {
':path': '/my-example/resources/script.js',
'user-agent': 'my-user-agent',
'x-my-header': 'second'
':method': 'GET',
':scheme': 'https',
':path': '/',
':authority': 'www.bar.com',
'cache-control': 'no-cache'
},
buffer: util.concat(test_headers.slice(3, 7).map(function(test) { return test.buffer; }))
buffer: util.concat(test_headers.slice(4, 9).map(function(test) { return test.buffer; }))
}, {
headers: {
':path': '/my-example/resources/script.js',
'user-agent': 'my-user-agent',
'x-my-header': ['third', 'second']
':method': 'GET',
':scheme': 'https',
':path': '/custom-path.css',
':authority': 'www.bar.com',
'custom-key': 'custom-value'
},
buffer: test_headers[7].buffer
buffer: util.concat(test_headers.slice(9, 13).map(function(test) { return test.buffer; }))
}, {
headers: {
':method': 'GET',
':scheme': 'https',
':path': '/custom-path.css',
':authority': ['www.foo.com', 'www.bar.com'],
'custom-key': 'custom-value'
},
buffer: test_headers[3].buffer
}, {
headers: {
':status': '200',

@@ -133,8 +251,40 @@ 'user-agent': 'my-user-agent',

describe('HuffmanTable', function() {
describe('method encode(buffer)', function() {
it('should return the Huffman encoded version of the input buffer', function() {
var table = HuffmanTable.requestHuffmanTable;
for (var decoded in test_huffman_request) {
var encoded = test_huffman_request[decoded];
expect(table.encode(new Buffer(decoded)).toString('hex')).to.equal(encoded);
}
table = HuffmanTable.responseHuffmanTable;
for (decoded in test_huffman_response) {
encoded = test_huffman_response[decoded];
expect(table.encode(new Buffer(decoded)).toString('hex')).to.equal(encoded);
}
});
})
describe('method decode(buffer)', function() {
it('should return the Huffman decoded version of the input buffer', function() {
var table = HuffmanTable.requestHuffmanTable;
for (var decoded in test_huffman_request) {
var encoded = test_huffman_request[decoded];
expect(table.decode(new Buffer(encoded, 'hex')).toString()).to.equal(decoded)
}
table = HuffmanTable.responseHuffmanTable;
for (decoded in test_huffman_response) {
encoded = test_huffman_response[decoded];
expect(table.decode(new Buffer(encoded, 'hex')).toString()).to.equal(decoded)
}
});
})
});
describe('HeaderSetCompressor', function() {
describe('static method .integer(I, N)', function() {
it('should return an array of buffers that represent the N-prefix coded form of the integer I', function() {
for (var i = 0; i < test_strings.length; i++) {
var test = test_strings[i];
expect(util.concat(HeaderSetCompressor.string(test.string))).to.deep.equal(test.buffer);
for (var i = 0; i < test_integers.length; i++) {
var test = test_integers[i];
test.buffer.cursor = 0;
expect(util.concat(HeaderSetCompressor.integer(test.I, test.N))).to.deep.equal(test.buffer);
}

@@ -145,13 +295,15 @@ });

it('should return an array of buffers that represent the encoded form of the string', function() {
var table = HuffmanTable.requestHuffmanTable;
for (var i = 0; i < test_strings.length; i++) {
var test = test_strings[i];
expect(util.concat(HeaderSetCompressor.string(test.string))).to.deep.equal(test.buffer);
expect(util.concat(HeaderSetCompressor.string(test.string, table))).to.deep.equal(test.buffer);
}
});
});
describe('static method .header({ name, value, indexing, substitution })', function() {
describe('static method .header({ name, value, index })', function() {
it('should return an array of buffers that represent the encoded form of the header', function() {
var table = HuffmanTable.requestHuffmanTable;
for (var i = 0; i < test_headers.length; i++) {
var test = test_headers[i];
expect(util.concat(HeaderSetCompressor.header(test.header))).to.deep.equal(test.buffer);
expect(util.concat(HeaderSetCompressor.header(test.header, table))).to.deep.equal(test.buffer);
}

@@ -175,6 +327,7 @@ });

it('should return the parsed string and increase the cursor property of buffer', function() {
var table = HuffmanTable.requestHuffmanTable;
for (var i = 0; i < test_strings.length; i++) {
var test = test_strings[i];
test.buffer.cursor = 0;
expect(HeaderSetDecompressor.string(test.buffer)).to.equal(test.string);
expect(HeaderSetDecompressor.string(test.buffer, table)).to.equal(test.string);
expect(test.buffer.cursor).to.equal(test.buffer.length);

@@ -186,6 +339,7 @@ }

it('should return the parsed header and increase the cursor property of buffer', function() {
var table = HuffmanTable.requestHuffmanTable;
for (var i = 0; i < test_headers.length; i++) {
var test = test_headers[i];
test.buffer.cursor = 0;
expect(HeaderSetDecompressor.header(test.buffer)).to.deep.equal(test.header);
expect(HeaderSetDecompressor.header(test.buffer, table)).to.deep.equal(test.header);
expect(test.buffer.cursor).to.equal(test.buffer.length);

@@ -200,8 +354,6 @@ }

var decompressor = new Decompressor(util.log, 'REQUEST');
var header_set = test_header_sets[0];
expect(decompressor.decompress(header_set.buffer)).to.deep.equal(header_set.headers);
header_set = test_header_sets[1];
expect(decompressor.decompress(header_set.buffer)).to.deep.equal(header_set.headers);
header_set = test_header_sets[2];
expect(decompressor.decompress(header_set.buffer)).to.deep.equal(header_set.headers);
for (var i = 0; i < 4; i++) {
var header_set = test_header_sets[i];
expect(decompressor.decompress(header_set.buffer)).to.deep.equal(header_set.headers);
}
});

@@ -238,4 +390,5 @@ });

var decompressor = new Decompressor(util.log, 'REQUEST');
var n = test_header_sets.length;
for (var i = 0; i < 10; i++) {
var headers = test_header_sets[i%4].headers;
var headers = test_header_sets[i%n].headers;
var compressed = compressor.compress(headers);

@@ -252,2 +405,3 @@ var decompressed = decompressor.decompress(compressed);

var decompressor = new Decompressor(util.log, 'RESPONSE');
var n = test_header_sets.length;
compressor.pipe(decompressor);

@@ -258,3 +412,3 @@ for (var i = 0; i < 10; i++) {

flags: {},
headers: test_header_sets[i%4].headers
headers: test_header_sets[i%n].headers
});

@@ -264,3 +418,3 @@ }

for (var j = 0; j < 10; j++) {
expect(decompressor.read().headers).to.deep.equal(test_header_sets[j%4].headers);
expect(decompressor.read().headers).to.deep.equal(test_header_sets[j%n].headers);
}

@@ -271,3 +425,17 @@ done();

});
describe('huffmanTable.decompress(huffmanTable.compress(buffer)) === buffer', function() {
it('should be true for any buffer', function() {
for (var i = 0; i < 10; i++) {
var buffer = [];
while (Math.random() > 0.1) {
buffer.push(Math.floor(Math.random() * 256))
}
buffer = new Buffer(buffer);
var table = HuffmanTable.requestHuffmanTable;
var result = table.decode(table.encode(buffer));
expect(result).to.deep.equal(buffer);
}
})
})
});
});

@@ -76,6 +76,8 @@ var expect = require('chai').expect;

type: 'SETTINGS',
flags: { },
flags: { ACK: false },
stream: 10,
settings: {
SETTINGS_HEADER_TABLE_SIZE: 0x12345678,
SETTINGS_ENABLE_PUSH: true,
SETTINGS_MAX_CONCURRENT_STREAMS: 0x01234567,

@@ -86,3 +88,5 @@ SETTINGS_INITIAL_WINDOW_SIZE: 0x89ABCDEF,

},
buffer: new Buffer('0018' + '04' + '00' + '0000000A' + '00' + '000004' + '01234567' +
buffer: new Buffer('0028' + '04' + '00' + '0000000A' + '00' + '000001' + '12345678' +
'00' + '000002' + '00000001' +
'00' + '000004' + '01234567' +
'00' + '000007' + '89ABCDEF' +

@@ -94,3 +98,3 @@ '00' + '00000A' + '00000001', 'hex')

type: 'PUSH_PROMISE',
flags: { END_PUSH_PROMISE: false },
flags: { RESERVED1: false, RESERVED2: false, END_PUSH_PROMISE: false },
stream: 15,

@@ -106,3 +110,3 @@

type: 'PING',
flags: { PONG: false },
flags: { ACK: false },
stream: 15,

@@ -137,3 +141,3 @@

type: 'CONTINUATION',
flags: { END_STREAM: false, RESERVED: false, END_HEADERS: true },
flags: { RESERVED1: false, RESERVED2: false, END_HEADERS: true },
stream: 10,

@@ -140,0 +144,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc