http2-protocol
Advanced tools
Comparing version 0.9.1 to 0.10.0
@@ -8,3 +8,3 @@ var fs = require('fs'); | ||
// Advertised protocol version | ||
var implementedVersion = 'HTTP-draft-09/2.0'; | ||
var implementedVersion = http2.ImplementedVersion; | ||
@@ -11,0 +11,0 @@ // Bunyan logger |
@@ -12,3 +12,3 @@ var fs = require('fs'); | ||
// Advertised protocol version | ||
var implementedVersion = 'HTTP-draft-09/2.0'; | ||
var implementedVersion = http2.ImplementedVersion; | ||
@@ -15,0 +15,0 @@ // Bunyan logger |
Version history | ||
=============== | ||
### 0.10.0 (2014-03-12) ### | ||
* Upgrade to the latest draft: [draft-ietf-httpbis-http2-10][draft-10] | ||
[draft-10]: http://tools.ietf.org/html/draft-ietf-httpbis-http2-10 | ||
### 0.9.1 (2014-01-11) ### | ||
@@ -5,0 +11,0 @@ |
@@ -231,3 +231,3 @@ // The implementation of the [HTTP/2 Header Compression][http2-compression] spec is separated from | ||
util.inherits(HeaderSetDecompressor, TransformStream); | ||
function HeaderSetDecompressor(log, table, huffmanTable) { | ||
function HeaderSetDecompressor(log, table) { | ||
TransformStream.call(this, { objectMode: true }); | ||
@@ -237,3 +237,2 @@ | ||
this._table = table; | ||
this._huffmanTable = huffmanTable; | ||
this._chunks = []; | ||
@@ -280,3 +279,4 @@ } | ||
// entails the following actions: | ||
// * The reference set is emptied. | ||
// * If the following byte starts with a set bit, the reference set is emptied. | ||
// * Else, reduce the size of the header table to the value encoded with a 7-bit prefix | ||
// * An _indexed representation_ corresponding to an entry _present_ in the reference set | ||
@@ -300,4 +300,9 @@ // entails the following actions: | ||
if (index == -1) { | ||
for (var i = 0; i < this._table.length; i++) { | ||
this._table[i].reference = false; | ||
if (rep.index) { | ||
for (var i = 0; i < this._table.length; i++) { | ||
this._table[i].reference = false; | ||
} | ||
} else { | ||
// Set a new maximum size | ||
this.setTableSizeLimit(rep.name); | ||
} | ||
@@ -359,3 +364,3 @@ } | ||
while (buffer.cursor < buffer.length) { | ||
this._execute(HeaderSetDecompressor.header(buffer, this._huffmanTable)); | ||
this._execute(HeaderSetDecompressor.header(buffer)); | ||
} | ||
@@ -388,3 +393,3 @@ | ||
util.inherits(HeaderSetCompressor, TransformStream); | ||
function HeaderSetCompressor(log, table, huffmanTable) { | ||
function HeaderSetCompressor(log, table) { | ||
TransformStream.call(this, { objectMode: true }); | ||
@@ -394,3 +399,2 @@ | ||
this._table = table; | ||
this._huffmanTable = huffmanTable; | ||
this.push = TransformStream.prototype.push.bind(this); | ||
@@ -404,3 +408,3 @@ } | ||
if (!rep.chunks) { | ||
rep.chunks = HeaderSetCompressor.header(rep, this._huffmanTable); | ||
rep.chunks = HeaderSetCompressor.header(rep); | ||
} | ||
@@ -716,3 +720,3 @@ rep.chunks.forEach(this.push); | ||
HuffmanTable.requestHuffmanTable = new HuffmanTable([ | ||
HuffmanTable.huffmanTable = new HuffmanTable([ | ||
'111111111111111111110111010', | ||
@@ -977,262 +981,2 @@ '111111111111111111110111011', | ||
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 ### | ||
@@ -1269,6 +1013,6 @@ // | ||
HeaderSetCompressor.string = function writeString(str, huffmanTable) { | ||
HeaderSetCompressor.string = function writeString(str) { | ||
str = new Buffer(str, 'utf8'); | ||
var huffman = huffmanTable.encode(str); | ||
var huffman = HuffmanTable.huffmanTable.encode(str); | ||
if (huffman.length < str.length) { | ||
@@ -1286,3 +1030,3 @@ var length = HeaderSetCompressor.integer(huffman.length, 7) | ||
HeaderSetDecompressor.string = function readString(buffer, huffmanTable) { | ||
HeaderSetDecompressor.string = function readString(buffer) { | ||
var huffman = buffer[buffer.cursor] & 128; | ||
@@ -1292,3 +1036,3 @@ var length = HeaderSetDecompressor.integer(buffer, 7); | ||
buffer.cursor += length; | ||
return (huffman ? huffmanTable.decode(encoded) : encoded).toString('utf8'); | ||
return (huffman ? HuffmanTable.huffmanTable.decode(encoded) : encoded).toString('utf8'); | ||
}; | ||
@@ -1369,3 +1113,3 @@ | ||
HeaderSetCompressor.header = function writeHeader(header, huffmanTable) { | ||
HeaderSetCompressor.header = function writeHeader(header) { | ||
var representation, buffers = []; | ||
@@ -1383,2 +1127,9 @@ | ||
buffers.push(HeaderSetCompressor.integer(header.value + 1, representation.prefix)); | ||
if (header.value == -1) { | ||
if (header.index) { | ||
buffers.push(HeaderSetCompressor.integer(0x80, 8)); | ||
} else { | ||
buffers.push(HeaderSetCompressor.integer(header.name, 7)); | ||
} | ||
} | ||
} | ||
@@ -1391,5 +1142,5 @@ | ||
buffers.push(HeaderSetCompressor.integer(0, representation.prefix)); | ||
buffers.push(HeaderSetCompressor.string(header.name, huffmanTable)); | ||
buffers.push(HeaderSetCompressor.string(header.name)); | ||
} | ||
buffers.push(HeaderSetCompressor.string(header.value, huffmanTable)); | ||
buffers.push(HeaderSetCompressor.string(header.value)); | ||
} | ||
@@ -1402,3 +1153,3 @@ | ||
HeaderSetDecompressor.header = function readHeader(buffer, huffmanTable) { | ||
HeaderSetDecompressor.header = function readHeader(buffer) { | ||
var representation, header = {}; | ||
@@ -1418,2 +1169,10 @@ | ||
header.index = false; | ||
if (header.value === -1) { | ||
if (buffer[buffer.cursor] & 0x80) { | ||
header.index = true; | ||
buffer.cursor += 1; | ||
} else { | ||
header.name = HeaderSetDecompressor.integer(buffer, 7); | ||
} | ||
} | ||
} | ||
@@ -1424,5 +1183,5 @@ | ||
if (header.name === -1) { | ||
header.name = HeaderSetDecompressor.string(buffer, huffmanTable); | ||
header.name = HeaderSetDecompressor.string(buffer); | ||
} | ||
header.value = HeaderSetDecompressor.string(buffer, huffmanTable); | ||
header.value = HeaderSetDecompressor.string(buffer); | ||
header.index = (representation === representations.literalIncremental); | ||
@@ -1469,4 +1228,2 @@ } | ||
assert((type === 'REQUEST') || (type === 'RESPONSE')); | ||
this._huffmanTable = (type === 'REQUEST') ? HuffmanTable.requestHuffmanTable | ||
: HuffmanTable.responseHuffmanTable; | ||
this._table = new HeaderTable(this._log); | ||
@@ -1484,3 +1241,3 @@ } | ||
Compressor.prototype.compress = function compress(headers) { | ||
var compressor = new HeaderSetCompressor(this._log, this._table, this._huffmanTable); | ||
var compressor = new HeaderSetCompressor(this._log, this._table); | ||
for (var name in headers) { | ||
@@ -1588,4 +1345,2 @@ var value = headers[name]; | ||
assert((type === 'REQUEST') || (type === 'RESPONSE')); | ||
this._huffmanTable = (type === 'REQUEST') ? HuffmanTable.requestHuffmanTable | ||
: HuffmanTable.responseHuffmanTable; | ||
this._table = new HeaderTable(this._log); | ||
@@ -1606,3 +1361,3 @@ | ||
Decompressor.prototype.decompress = function decompress(block) { | ||
var decompressor = new HeaderSetDecompressor(this._log, this._table, this._huffmanTable); | ||
var decompressor = new HeaderSetDecompressor(this._log, this._table); | ||
decompressor.end(block); | ||
@@ -1609,0 +1364,0 @@ |
@@ -192,4 +192,4 @@ var assert = require('assert'); | ||
// * handling stream errors as connection errors | ||
stream.on('error', this.emit.bind(this, 'error')); | ||
// * forwarding connection errors from streams | ||
stream.on('connectionError', this.emit.bind(this, 'error')); | ||
@@ -381,3 +381,2 @@ return id; | ||
var defaultSettings = { | ||
SETTINGS_FLOW_CONTROL_OPTIONS: true | ||
}; | ||
@@ -568,11 +567,5 @@ | ||
stream.upstream.setInitialWindow(this._initialStreamWindowSize); | ||
if (this._remoteFlowControlDisabled) { | ||
stream.upstream.disableRemoteFlowControl(); | ||
} | ||
}); | ||
this.on('RECEIVING_SETTINGS_INITIAL_WINDOW_SIZE', this._setInitialStreamWindowSize); | ||
this.on('RECEIVING_SETTINGS_FLOW_CONTROL_OPTIONS', this._setLocalFlowControl); | ||
this.on('SENDING_SETTINGS_FLOW_CONTROL_OPTIONS', this._setRemoteFlowControl); | ||
this._streamIds[0].upstream.setInitialWindow = function noop() {}; | ||
this._streamIds[0].upstream.disableRemoteFlowControl = function noop() {}; | ||
}; | ||
@@ -599,25 +592,1 @@ | ||
}; | ||
// `_setStreamFlowControl()` may be used to disable/enable flow control. In practice, it is just | ||
// for turning off flow control since it can not be turned on. | ||
Connection.prototype._setLocalFlowControl = function _setLocalFlowControl(disable) { | ||
if (disable) { | ||
this._increaseWindow(Infinity); | ||
this._setInitialStreamWindowSize(Infinity); | ||
} else if (this._initialStreamWindowSize === Infinity) { | ||
this._log.error('Trying to re-enable flow control after it was turned off.'); | ||
this.emit('error', 'FLOW_CONTROL_ERROR'); | ||
} | ||
}; | ||
Connection.prototype._setRemoteFlowControl = function _setRemoteFlowControl(disable) { | ||
if (disable) { | ||
this.disableRemoteFlowControl(); | ||
this._streamIds.forEach(function(stream) { | ||
stream.upstream.disableRemoteFlowControl(); | ||
}); | ||
} else if (this._remoteFlowControlDisabled) { | ||
this._log.error('Trying to re-enable flow control after it was turned off.'); | ||
throw new Error('Trying to re-enable flow control after it was turned off.'); | ||
} | ||
}; |
@@ -22,7 +22,3 @@ var assert = require('assert'); | ||
// | ||
// * **disableRemoteFlowControl()**: sends a WINDOW_UPDATE signaling that we don't want flow control | ||
// | ||
// * **disableLocalFlowControl()**: disables flow control for outgoing frames | ||
// | ||
// [1]: http://tools.ietf.org/html/draft-ietf-httpbis-http2-09#section-6.9.2 | ||
// [1]: http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-6.9.2 | ||
@@ -72,3 +68,2 @@ // API for child classes | ||
this._received = 0; | ||
this._remoteFlowControlDisabled = false; | ||
} | ||
@@ -94,3 +89,3 @@ Flow.prototype = Object.create(Duplex.prototype, { constructor: { value: Flow } }); | ||
if ((frame.type === 'DATA') && (frame.data.length > 0) && !this._remoteFlowControlDisabled) { | ||
if ((frame.type === 'DATA') && (frame.data.length > 0)) { | ||
this._receive(frame, function() { | ||
@@ -119,3 +114,3 @@ this._received += frame.data.length; | ||
delete this._restoreWindowTimer; | ||
if (!this._ended && !this._remoteFlowControlDisabled && (this._received > 0)) { | ||
if (!this._ended && (this._received > 0)) { | ||
this.push({ | ||
@@ -131,8 +126,2 @@ type: 'WINDOW_UPDATE', | ||
// Must be called after sending a SETTINGS frame that turns off flow control on the remote side. | ||
Flow.prototype.disableRemoteFlowControl = function disableRemoteFlowControl() { | ||
this._log.debug('Turning off remote flow control'); | ||
this._remoteFlowControlDisabled = true; | ||
}; | ||
// Outgoing frames - sending procedure | ||
@@ -363,6 +352,1 @@ // ----------------------------------- | ||
}; | ||
// Flow control for outgoing frames can be disabled by the peer with various methods. | ||
Flow.prototype.disableLocalFlowControl = function disableLocalFlowControl() { | ||
this._increaseWindow(Infinity); | ||
}; |
@@ -15,2 +15,3 @@ // The framer consists of two [Transform Stream][1] subclasses that operate in [object mode][2]: | ||
var MAX_PAYLOAD_SIZE = 16383; | ||
var WINDOW_UPDATE_PAYLOAD_SIZE = 4; | ||
@@ -70,3 +71,4 @@ // Serializer | ||
function Deserializer(log, sizeLimit) { | ||
function Deserializer(log, sizeLimit, role) { | ||
this._role = role; | ||
this._log = log.child({ component: 'deserializer' }); | ||
@@ -132,6 +134,6 @@ this._sizeLimit = sizeLimit || MAX_PAYLOAD_SIZE; | ||
if (this._frame.type) { | ||
var error = Deserializer[this._frame.type](this._buffer, this._frame); | ||
var error = Deserializer[this._frame.type](this._buffer, this._frame, this._role); | ||
if (error) { | ||
this._log.error('Incoming frame parsing error: ' + error); | ||
this.emit('error', 'PROTOCOL_ERROR'); | ||
this.emit('error', error); | ||
} else { | ||
@@ -142,3 +144,4 @@ this._log.trace({ frame: this._frame }, 'Incoming frame'); | ||
} else { | ||
this._log.warn({ frame: this._frame }, 'Unknown type incoming frame'); | ||
this._log.error('Unknown type incoming frame'); | ||
this.emit('error', 'PROTOCOL_ERROR'); | ||
} | ||
@@ -152,3 +155,3 @@ this._next(COMMON_HEADER_SIZE); | ||
// [Frame Header](http://tools.ietf.org/html/draft-ietf-httpbis-http2-09#section-4.1) | ||
// [Frame Header](http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-4.1) | ||
// -------------------------------------------------------------- | ||
@@ -267,3 +270,3 @@ // | ||
// [DATA Frames](http://tools.ietf.org/html/draft-ietf-httpbis-http2-09#section-6.1) | ||
// [DATA Frames](http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-6.1) | ||
// ------------------------------------------------------------ | ||
@@ -279,8 +282,16 @@ // | ||
// identified stream. | ||
// * RESERVED (0x2): | ||
// Bit 2 is reserved for future use. | ||
// * END_SEGMENT (0x2): | ||
// Bit 2 being set indicates that this frame is the last for the current segment. Intermediaries | ||
// MUST NOT coalesce frames across a segment boundary and MUST preserve segment boundaries when | ||
// forwarding frames. | ||
// * PAD_LOW (0x10): | ||
// Bit 5 being set indicates that the Pad Low field is present. | ||
// * PAD_HIGH (0x20): | ||
// Bit 6 being set indicates that the Pad High field is present. This bit MUST NOT be set unless | ||
// the PAD_LOW flag is also set. Endpoints that receive a frame with PAD_HIGH set and PAD_LOW | ||
// cleared MUST treat this as a connection error of type PROTOCOL_ERROR. | ||
frameTypes[0x0] = 'DATA'; | ||
frameFlags.DATA = ['END_STREAM', 'RESERVED']; | ||
frameFlags.DATA = ['END_STREAM', 'END_SEGMENT', 'RESERVED4', 'RESERVED8', 'PAD_LOW', 'PAD_HIGH']; | ||
@@ -294,6 +305,22 @@ typeSpecificAttributes.DATA = ['data']; | ||
Deserializer.DATA = function readData(buffer, frame) { | ||
frame.data = buffer; | ||
var dataOffset = 0; | ||
var paddingLength = 0; | ||
if (frame.flags.PAD_LOW) { | ||
if (frame.flags.PAD_HIGH) { | ||
paddingLength = (buffer.readUInt8(dataOffset) & 0xff) * 256; | ||
dataOffset += 1; | ||
} | ||
paddingLength += (buffer.readUInt8(dataOffset) & 0xff); | ||
dataOffset += 1; | ||
} else if (frame.flags.PAD_HIGH) { | ||
return 'DATA frame got PAD_HIGH without PAD_LOW'; | ||
} | ||
if (paddingLength) { | ||
frame.data = buffer.slice(dataOffset, -1 * paddingLength); | ||
} else { | ||
frame.data = buffer.slice(dataOffset); | ||
} | ||
}; | ||
// [HEADERS](http://tools.ietf.org/html/draft-ietf-httpbis-http2-09#section-6.2) | ||
// [HEADERS](http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-6.2) | ||
// -------------------------------------------------------------- | ||
@@ -308,4 +335,6 @@ // | ||
// identified stream. | ||
// * RESERVED (0x2): | ||
// Bit 2 is reserved for future use. | ||
// * END_SEGMENT (0x2): | ||
// Bit 2 being set indicates that this frame is the last for the current segment. Intermediaries | ||
// MUST NOT coalesce frames across a segment boundary and MUST preserve segment boundaries when | ||
// forwarding frames. | ||
// * END_HEADERS (0x4): | ||
@@ -317,6 +346,12 @@ // The END_HEADERS bit indicates that this frame contains the entire payload necessary to provide | ||
// bit and a 31-bit priority. | ||
// * PAD_LOW (0x10): | ||
// Bit 5 being set indicates that the Pad Low field is present. | ||
// * PAD_HIGH (0x20): | ||
// Bit 6 being set indicates that the Pad High field is present. This bit MUST NOT be set unless | ||
// the PAD_LOW flag is also set. Endpoints that receive a frame with PAD_HIGH set and PAD_LOW | ||
// cleared MUST treat this as a connection error of type PROTOCOL_ERROR. | ||
frameTypes[0x1] = 'HEADERS'; | ||
frameFlags.HEADERS = ['END_STREAM', 'RESERVED', 'END_HEADERS', 'PRIORITY']; | ||
frameFlags.HEADERS = ['END_STREAM', 'END_SEGMENT', 'END_HEADERS', 'PRIORITY', 'PAD_LOW', 'PAD_HIGH']; | ||
@@ -346,11 +381,26 @@ typeSpecificAttributes.HEADERS = ['priority', 'headers', 'data']; | ||
Deserializer.HEADERS = function readHeadersPriority(buffer, frame) { | ||
var dataOffset = 0; | ||
var paddingLength = 0; | ||
if (frame.flags.PAD_LOW) { | ||
if (frame.flags.PAD_HIGH) { | ||
paddingLength = (buffer.readUInt8(dataOffset) & 0xff) * 256; | ||
dataOffset += 1; | ||
} | ||
paddingLength += (buffer.readUInt8(dataOffset) & 0xff); | ||
dataOffset += 1; | ||
} else if (frame.flags.PAD_HIGH) { | ||
return 'HEADERS frame got PAD_HIGH without PAD_LOW'; | ||
} | ||
if (frame.flags.PRIORITY) { | ||
frame.priority = buffer.readUInt32BE(0) & 0x7fffffff; | ||
frame.data = buffer.slice(4); | ||
frame.priority = buffer.readUInt32BE(dataOffset) & 0x7fffffff; | ||
dataOffset += 4; | ||
} | ||
if (paddingLength) { | ||
frame.data = buffer.slice(dataOffset, -1 * paddingLength); | ||
} else { | ||
frame.data = buffer; | ||
frame.data = buffer.slice(dataOffset); | ||
} | ||
}; | ||
// [PRIORITY](http://tools.ietf.org/html/draft-ietf-httpbis-http2-09#section-6.3) | ||
// [PRIORITY](http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-6.3) | ||
// ------------------------------------------------------- | ||
@@ -386,3 +436,3 @@ // | ||
// [RST_STREAM](http://tools.ietf.org/html/draft-ietf-httpbis-http2-09#section-6.4) | ||
// [RST_STREAM](http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-6.4) | ||
// ----------------------------------------------------------- | ||
@@ -421,3 +471,3 @@ // | ||
// [SETTINGS](http://tools.ietf.org/html/draft-ietf-httpbis-http2-09#section-6.5) | ||
// [SETTINGS](http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-6.5) | ||
// ------------------------------------------------------- | ||
@@ -440,3 +490,3 @@ // | ||
// The payload of a SETTINGS frame consists of zero or more settings. Each setting consists of an | ||
// 8-bit reserved field, an unsigned 24-bit setting identifier, and an unsigned 32-bit value. | ||
// 8-bit identifier, and an unsigned 32-bit value. | ||
// | ||
@@ -446,6 +496,6 @@ // 0 1 2 3 | ||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
// | Reserved(8) | Setting Identifier (24) | | ||
// +---------------+-----------------------------------------------+ | ||
// | Value (32) | | ||
// +---------------------------------------------------------------+ | ||
// | Identifier(8) | Value (32) | | ||
// +-----------------+---------------------------------------------+ | ||
// ...Value | | ||
// +-----------------+ | ||
// | ||
@@ -470,6 +520,6 @@ // Each setting in a SETTINGS frame replaces the existing value for that setting. Settings are | ||
var buffer = new Buffer(settings.length * 8); | ||
var buffer = new Buffer(settings.length * 5); | ||
for (var i = 0; i < settings.length; i++) { | ||
buffer.writeUInt32BE(settings[i].id & 0xffffff, i*8); | ||
buffer.writeUInt32BE(settings[i].value, i*8 + 4); | ||
buffer.writeUInt8(settings[i].id & 0xff, i*5); | ||
buffer.writeUInt32BE(settings[i].value, i*5 + 1); | ||
} | ||
@@ -480,16 +530,27 @@ | ||
Deserializer.SETTINGS = function readSettings(buffer, frame) { | ||
Deserializer.SETTINGS = function readSettings(buffer, frame, role) { | ||
frame.settings = {}; | ||
// Receipt of a SETTINGS frame with the ACK flag set and a length | ||
// field value other than 0 MUST be treated as a connection error | ||
// (Section 5.4.1) of type FRAME_SIZE_ERROR. | ||
if(frame.flags.ACK && buffer.length != 0) { | ||
return 'FRAME_SIZE_ERROR'; | ||
} | ||
if (buffer.length % 8 !== 0) { | ||
return 'Invalid SETTINGS frame'; | ||
if (buffer.length % 5 !== 0) { | ||
return 'PROTOCOL_ERROR'; | ||
} | ||
for (var i = 0; i < buffer.length / 8; i++) { | ||
var id = buffer.readUInt32BE(i*8) & 0xffffff; | ||
for (var i = 0; i < buffer.length / 5; i++) { | ||
var id = buffer.readUInt8(i*5) & 0xff; | ||
var setting = definedSettings[id]; | ||
if (setting) { | ||
var value = buffer.readUInt32BE(i*8 + 4); | ||
if (role == 'CLIENT' && setting.name == 'SETTINGS_ENABLE_PUSH') { | ||
return 'SETTINGS frame on client got SETTINGS_ENABLE_PUSH'; | ||
} | ||
var value = buffer.readUInt32BE(i*5 + 1); | ||
frame.settings[setting.name] = setting.flag ? Boolean(value & 0x1) : value; | ||
} else { | ||
/* Unknown setting, ignoring */ | ||
/* Unknown setting, protocol error */ | ||
return 'SETTINGS frame got unknown setting type'; | ||
} | ||
@@ -515,15 +576,9 @@ } | ||
// indicates the maximum number of concurrent streams that the sender will allow. | ||
definedSettings[4] = { name: 'SETTINGS_MAX_CONCURRENT_STREAMS', flag: false }; | ||
definedSettings[3] = { name: 'SETTINGS_MAX_CONCURRENT_STREAMS', flag: false }; | ||
// * SETTINGS_INITIAL_WINDOW_SIZE (7): | ||
// indicates the sender's initial stream window size (in bytes) for new streams. | ||
definedSettings[7] = { name: 'SETTINGS_INITIAL_WINDOW_SIZE', flag: false }; | ||
definedSettings[4] = { name: 'SETTINGS_INITIAL_WINDOW_SIZE', flag: false }; | ||
// * SETTINGS_FLOW_CONTROL_OPTIONS (10): | ||
// indicates that streams directed to the sender will not be subject to flow control. The least | ||
// significant bit (0x1) is set to indicate that new streams are not flow controlled. All other | ||
// bits are reserved. | ||
definedSettings[10] = { name: 'SETTINGS_FLOW_CONTROL_OPTIONS', flag: true }; | ||
// [PUSH_PROMISE](http://tools.ietf.org/html/draft-ietf-httpbis-http2-09#section-6.6) | ||
// [PUSH_PROMISE](http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-6.6) | ||
// --------------------------------------------------------------- | ||
@@ -574,3 +629,3 @@ // | ||
// [PING](http://tools.ietf.org/html/draft-ietf-httpbis-http2-09#section-6.7) | ||
// [PING](http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-6.7) | ||
// ----------------------------------------------- | ||
@@ -600,3 +655,3 @@ // | ||
if (buffer.length !== 8) { | ||
return 'Invalid size PING frame'; | ||
return 'FRAME_SIZE_ERROR'; | ||
} | ||
@@ -606,3 +661,3 @@ frame.data = buffer; | ||
// [GOAWAY](http://tools.ietf.org/html/draft-ietf-httpbis-http2-09#section-6.8) | ||
// [GOAWAY](http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-6.8) | ||
// --------------------------------------------------- | ||
@@ -654,10 +709,10 @@ // | ||
// [WINDOW_UPDATE](http://tools.ietf.org/html/draft-ietf-httpbis-http2-09#section-6.9) | ||
// [WINDOW_UPDATE](http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-6.9) | ||
// ----------------------------------------------------------------- | ||
// | ||
// The WINDOW_UPDATE frame (type=0x9) is used to implement flow control. | ||
// The WINDOW_UPDATE frame (type=0x8) is used to implement flow control. | ||
// | ||
// The WINDOW_UPDATE frame does not define any flags. | ||
frameTypes[0x9] = 'WINDOW_UPDATE'; | ||
frameTypes[0x8] = 'WINDOW_UPDATE'; | ||
@@ -684,6 +739,9 @@ frameFlags.WINDOW_UPDATE = []; | ||
Deserializer.WINDOW_UPDATE = function readWindowUpdate(buffer, frame) { | ||
if (buffer.length !== WINDOW_UPDATE_PAYLOAD_SIZE) { | ||
return 'FRAME_SIZE_ERROR'; | ||
} | ||
frame.window_size = buffer.readUInt32BE(0) & 0x7fffffff; | ||
}; | ||
// [CONTINUATION](http://tools.ietf.org/html/draft-ietf-httpbis-http2-09#section-6.10) | ||
// [CONTINUATION](http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-6.10) | ||
// ------------------------------------------------------------ | ||
@@ -698,6 +756,12 @@ // | ||
// necessary to provide a complete set of headers. | ||
// * PAD_LOW (0x10): | ||
// Bit 5 being set indicates that the Pad Low field is present. | ||
// * PAD_HIGH (0x20): | ||
// Bit 6 being set indicates that the Pad High field is present. This bit MUST NOT be set unless | ||
// the PAD_LOW flag is also set. Endpoints that receive a frame with PAD_HIGH set and PAD_LOW | ||
// cleared MUST treat this as a connection error of type PROTOCOL_ERROR. | ||
frameTypes[0xA] = 'CONTINUATION'; | ||
frameTypes[0x9] = 'CONTINUATION'; | ||
frameFlags.CONTINUATION = ['RESERVED1', 'RESERVED2', 'END_HEADERS']; | ||
frameFlags.CONTINUATION = ['RESERVED1', 'RESERVED2', 'END_HEADERS', 'RESERVED8', 'PAD_LOW', 'PAD_HIGH']; | ||
@@ -711,6 +775,22 @@ typeSpecificAttributes.CONTINUATION = ['headers', 'data']; | ||
Deserializer.CONTINUATION = function readContinuation(buffer, frame) { | ||
frame.data = buffer; | ||
var dataOffset = 0; | ||
var paddingLength = 0; | ||
if (frame.flags.PAD_LOW) { | ||
if (frame.flags.PAD_HIGH) { | ||
paddingLength = (buffer.readUInt8(dataOffset) & 0xff) * 256; | ||
dataOffset += 1; | ||
} | ||
paddingLength += (buffer.readUInt8(dataOffset) & 0xff); | ||
dataOffset += 1; | ||
} else if (frame.flags.PAD_HIGH) { | ||
return 'CONTINUATION frame got PAD_HIGH without PAD_LOW'; | ||
} | ||
if (paddingLength) { | ||
frame.data = buffer.slice(dataOffset, -1 * paddingLength); | ||
} else { | ||
frame.data = buffer.slice(dataOffset); | ||
} | ||
}; | ||
// [Error Codes](http://tools.ietf.org/html/draft-ietf-httpbis-http2-09#section-7) | ||
// [Error Codes](http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-7) | ||
// ------------------------------------------------------------ | ||
@@ -717,0 +797,0 @@ |
@@ -1,2 +0,2 @@ | ||
// [node-http2-protocol][homepage] is an implementation of the [HTTP/2 (draft 09)][http2] | ||
// [node-http2-protocol][homepage] is an implementation of the [HTTP/2 (draft 10)][http2] | ||
// framing layer for [node.js][node]. | ||
@@ -31,6 +31,6 @@ // | ||
// [homepage]: https://github.com/molnarg/node-http2 | ||
// [http2]: http://tools.ietf.org/html/draft-ietf-httpbis-http2-09 | ||
// [http2-connheader]: http://tools.ietf.org/html/draft-ietf-httpbis-http2-09#section-3.5 | ||
// [http2-stream]: http://tools.ietf.org/html/draft-ietf-httpbis-http2-09#section-5 | ||
// [http2-streamstate]: http://tools.ietf.org/html/draft-ietf-httpbis-http2-09#section-5.1 | ||
// [http2]: http://tools.ietf.org/html/draft-ietf-httpbis-http2-10 | ||
// [http2-connheader]: http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-3.5 | ||
// [http2-stream]: http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-5 | ||
// [http2-streamstate]: http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-5.1 | ||
// [node]: http://nodejs.org/ | ||
@@ -41,2 +41,4 @@ // [node-stream]: http://nodejs.org/api/stream.html | ||
exports.ImplementedVersion = 'h2-10'; | ||
exports.Endpoint = require('./endpoint').Endpoint; | ||
@@ -43,0 +45,0 @@ |
@@ -325,3 +325,3 @@ var assert = require('assert'); | ||
// [Stream States](http://tools.ietf.org/html/draft-ietf-httpbis-http2-09#section-5.1) | ||
// [Stream States](http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-5.1) | ||
// ---------------- | ||
@@ -383,3 +383,4 @@ // | ||
var receiving = !sending; | ||
var error = undefined; | ||
var connectionError; | ||
var streamError; | ||
@@ -415,3 +416,3 @@ var DATA = false, HEADERS = false, PRIORITY = false; | ||
} else { | ||
error = 'PROTOCOL_ERROR'; | ||
connectionError = 'PROTOCOL_ERROR'; | ||
} | ||
@@ -437,3 +438,3 @@ break; | ||
} else { | ||
error = 'PROTOCOL_ERROR'; | ||
connectionError = 'PROTOCOL_ERROR'; | ||
} | ||
@@ -457,3 +458,3 @@ break; | ||
} else { | ||
error = 'PROTOCOL_ERROR'; | ||
connectionError = 'PROTOCOL_ERROR'; | ||
} | ||
@@ -493,3 +494,3 @@ break; | ||
} else { | ||
error = 'PROTOCOL_ERROR'; | ||
connectionError = 'PROTOCOL_ERROR'; | ||
} | ||
@@ -514,3 +515,3 @@ break; | ||
} else { | ||
error = 'PROTOCOL_ERROR'; | ||
connectionError = 'PROTOCOL_ERROR'; | ||
} | ||
@@ -545,3 +546,3 @@ break; | ||
} else { | ||
error = 'STREAM_CLOSED'; | ||
streamError = 'STREAM_CLOSED'; | ||
} | ||
@@ -565,3 +566,3 @@ break; | ||
// The state of the stream becomes "reserved (remote)". | ||
if (PUSH_PROMISE && !error) { | ||
if (PUSH_PROMISE && !connectionError && !streamError) { | ||
/* This assertion must hold, because _transition is called immediately when a frame is written | ||
@@ -588,5 +589,5 @@ to the stream. If it would be called when a frame gets out of the input queue, the state | ||
// Common error handling. | ||
if (error) { | ||
if (connectionError || streamError) { | ||
var info = { | ||
error: error, | ||
error: connectionError, | ||
frame: frame, | ||
@@ -604,2 +605,4 @@ state: this.state, | ||
// * In case of a serious problem, emitting and error and letting someone else handle it | ||
// (e.g. closing the connection) | ||
// * When receiving something invalid, sending an RST_STREAM using the `reset` method. | ||
@@ -609,3 +612,8 @@ // This will automatically cause a transition to the CLOSED state. | ||
this._log.error(info, 'Received illegal frame.'); | ||
this.emit('error', error); | ||
if (connectionError) { | ||
this.emit('connectionError', connectionError); | ||
} else { | ||
this.reset(streamError); | ||
this.emit('error', streamError) | ||
} | ||
} | ||
@@ -612,0 +620,0 @@ } |
{ | ||
"name": "http2-protocol", | ||
"version": "0.9.1", | ||
"version": "0.10.0", | ||
"description": "A JavaScript implementation of the HTTP/2 framing layer", | ||
@@ -37,3 +37,4 @@ "main": "lib/index.js", | ||
"Nick Hurley", | ||
"Mike Belshe" | ||
"Mike Belshe", | ||
"vsemogutor" | ||
], | ||
@@ -40,0 +41,0 @@ "license": "MIT", |
node-http2-protocol | ||
=================== | ||
An HTTP/2 ([draft-ietf-httpbis-http2-09](http://tools.ietf.org/html/draft-ietf-httpbis-http2-09)) | ||
An HTTP/2 ([draft-ietf-httpbis-http2-10](http://tools.ietf.org/html/draft-ietf-httpbis-http2-10)) | ||
framing layer implementaion for node.js. | ||
@@ -55,6 +55,6 @@ | ||
``` | ||
Statements : 92.43% ( 1257/1360 ) | ||
Branches : 86.36% ( 500/579 ) | ||
Functions : 90.12% ( 146/162 ) | ||
Lines : 92.39% ( 1251/1354 ) | ||
Statements : 91.86% ( 1276/1389 ) | ||
Branches : 85.62% ( 524/612 ) | ||
Functions : 92.31% ( 144/156 ) | ||
Lines : 91.83% ( 1270/1383 ) | ||
``` | ||
@@ -74,2 +74,3 @@ | ||
* Mike Belshe | ||
* vsemogutor | ||
@@ -76,0 +77,0 @@ Special thanks to Google for financing the development of this module as part of their [Summer of |
@@ -52,10 +52,10 @@ var expect = require('chai').expect; | ||
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', | ||
'302': '98a7', | ||
'private': '73d5cd111f', | ||
'Mon, 21 OCt 2013 20:13:21 GMT': 'ef6b3a7a0e6e8fa7647a0e534dd072fb0d37b0e6e8f777f8ff', | ||
': https://www.bar.com': 'f6746718ba1ec00db6d897a1e44b74', | ||
'200': '394b', | ||
'Mon, 21 OCt 2013 20:13:22 GMT': 'ef6b3a7a0e6e8fa7647a0e534dd072fb0d37b0e7e8f777f8ff', | ||
'https://www.bar.com': 'ce31743d801b6db12f43c896e9', | ||
'gzip': 'cbd54e', | ||
'foo=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\ | ||
@@ -68,17 +68,18 @@ AAAAAAAAAAAAAAAAAAAAAAAAAALASDJKHQKBZXOQWEOPIUAXQWEOIUAXLJKHQWOEIUAL\ | ||
ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ1234 m\ | ||
ax-age=3600; version=1': 'df7dfb36eddbb76eddbb76eddbb76eddbb76eddbb76eddbb76\ | ||
eddbb76eddbb76eddbb76eddbb76eddbb76eddbb76eddbb76eddbb76eddbb76eddbb\ | ||
76eddbb76eddbb7e3b69ecf0fe7e1fd7f3d5fe7f7e5fd79f6f97cbbfe9b7fbfebcfb\ | ||
7cbbfe9b7fbf8f87f3f0febcfcbb7bfe9b7e3fd79f6f977fd36ff7f1febb7e9b7fbf\ | ||
8fc7f9f0db4f67f5e7dbe5f4efdbfdf891a13f1db4f6787f3f0febf9eaff3fbf2feb\ | ||
cfb7cbe5dff4dbfdff5e7dbe5dff4dbfdfc7c3f9f87f5e7e5dbdff4dbf1febcfb7cb\ | ||
bfe9b7fbf8ff5dbf4dbfdfc7e3fcf86da7b3faf3edf2fa77edfefc48d09f8eda7b3c\ | ||
3f9f87f5fcf57f9fdf97f5e7dbe5f2effa6dfeffaf3edf2effa6dfefe3e1fcfc3faf\ | ||
3f2edeffa6df8ff5e7dbe5dff4dbfdfc7faedfa6dfefe3f1fe7c36d3d9fd79f6f97d\ | ||
3bf6ff7e24684fc76d3d9e1fcfc3fafe7abfcfefcbfaf3edf2f977fd36ff7fd79f6f\ | ||
977fd36ff7f1f0fe7e1fd79f976f7fd36fc7faf3edf2effa6dfefe3fd76fd36ff7f1\ | ||
f8ff3e1b69ecfebcfb7cbe9dfb7fbf123427fcff3fcff3fcff3fcff3fcff3fcff3fc\ | ||
ff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3\ | ||
ax-age=3600; version=1': 'c5adb77efdfbf7efdfbf7efdfbf7efdfbf7efdfbf7\ | ||
efdfbf7efdfbf7efdfbf7efdfbf7efdfbf7efdfbf7efdfbf7efdfbf7efdfbf7efdfb\ | ||
f7efdfbf7efdfbf7efdfbfe5bfc3b7e3fdfbfedfdf5ff9fbfa7dbf5ddf4fafc3f1bf\ | ||
f7f6fd777d3e1f8dffbf97c7fbf7fdbf5f4eef87e37fcbedfaeefa7c3f1bff7f2fb7\ | ||
77e37fefe5f2fefe3bfc3b7edfaeefa7e3e1bff7f331e69fe5bfc3b7e3fdfbfedfdf\ | ||
5ff9fbfa7dbf5ddf4fafc3f1bff7f6fd777d3e1f8dffbf97c7fbf7fdbf5f4eef87e3\ | ||
7fcbedfaeefa7c3f1bff7f2fb777e37fefe5f2fefe3bfc3b7edfaeefa7e3e1bff7f3\ | ||
31e69fe5bfc3b7e3fdfbfedfdf5ff9fbfa7dbf5ddf4fafc3f1bff7f6fd777d3e1f8d\ | ||
ffbf97c7fbf7fdbf5f4eef87e37fcbedfaeefa7c3f1bff7f2fb777e37fefe5f2fefe\ | ||
3bfc3b7edfaeefa7e3e1bff7f331e69fe5bfc3b7e3fdfbfedfdf5ff9fbfa7dbf5ddf\ | ||
4fafc3f1bff7f6fd777d3e1f8dffbf97c7fbf7fdbf5f4eef87e37fcbedfaeefa7c3f\ | ||
1bff7f2fb777e37fefe5f2fefe3bfc3b7edfaeefa7e3e1bff7f331e69ffcff3fcff3\ | ||
fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcf\ | ||
f3fcff3fcff08d090b5fd237f086c44a23ef0e70c72b2fbb617f', | ||
f3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3f\ | ||
cff3fcff3fcff3fcff3fcff3fcff3fcff0c79a7e8d11e72a321b66a4a5eae8e62f82\ | ||
9acb4d', | ||
'foo=ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ\ | ||
@@ -91,17 +92,18 @@ ZZZZZZZZZZZZZZZZZZZZZZZZZZLASDJKHQKBZXOQWEOPIUAXQWEOIUAXLJKHQWOEIUAL\ | ||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1234 m\ | ||
ax-age=3600; version=1': 'df7dfb3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcf\ | ||
f3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3f\ | ||
ax-age=3600; version=1': 'c5adb7fcff3fcff3fcff3fcff3fcff3fcff3fcff3f\ | ||
cff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff\ | ||
3e3b69ecf0fe7e1fd7f3d5fe7f7e5fd79f6f97cbbfe9b7fbfebcfb7cbbfe9b7fbf8f\ | ||
87f3f0febcfcbb7bfe9b7e3fd79f6f977fd36ff7f1febb7e9b7fbf8fc7f9f0db4f67\ | ||
f5e7dbe5f4efdbfdf891a13f1db4f6787f3f0febf9eaff3fbf2febcfb7cbe5dff4db\ | ||
fdff5e7dbe5dff4dbfdfc7c3f9f87f5e7e5dbdff4dbf1febcfb7cbbfe9b7fbf8ff5d\ | ||
bf4dbfdfc7e3fcf86da7b3faf3edf2fa77edfefc48d09f8eda7b3c3f9f87f5fcf57f\ | ||
9fdf97f5e7dbe5f2effa6dfeffaf3edf2effa6dfefe3e1fcfc3faf3f2edeffa6df8f\ | ||
f5e7dbe5dff4dbfdfc7faedfa6dfefe3f1fe7c36d3d9fd79f6f97d3bf6ff7e24684f\ | ||
c76d3d9e1fcfc3fafe7abfcfefcbfaf3edf2f977fd36ff7fd79f6f977fd36ff7f1f0\ | ||
fe7e1fd79f976f7fd36fc7faf3edf2effa6dfefe3fd76fd36ff7f1f8ff3e1b69ecfe\ | ||
bcfb7cbe9dfb7fbf1234276eddbb76eddbb76eddbb76eddbb76eddbb76eddbb76edd\ | ||
bb76eddbb76eddbb76eddbb76eddbb76eddbb76eddbb76eddbb76eddbb76eddbb76e\ | ||
ddbb76eddbb48d090b5fd237f086c44a23ef0e70c72b2fbb617f' | ||
3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fcff3fc\ | ||
ff3fcff3e5bfc3b7e3fdfbfedfdf5ff9fbfa7dbf5ddf4fafc3f1bff7f6fd777d3e1f\ | ||
8dffbf97c7fbf7fdbf5f4eef87e37fcbedfaeefa7c3f1bff7f2fb777e37fefe5f2fe\ | ||
fe3bfc3b7edfaeefa7e3e1bff7f331e69fe5bfc3b7e3fdfbfedfdf5ff9fbfa7dbf5d\ | ||
df4fafc3f1bff7f6fd777d3e1f8dffbf97c7fbf7fdbf5f4eef87e37fcbedfaeefa7c\ | ||
3f1bff7f2fb777e37fefe5f2fefe3bfc3b7edfaeefa7e3e1bff7f331e69fe5bfc3b7\ | ||
e3fdfbfedfdf5ff9fbfa7dbf5ddf4fafc3f1bff7f6fd777d3e1f8dffbf97c7fbf7fd\ | ||
bf5f4eef87e37fcbedfaeefa7c3f1bff7f2fb777e37fefe5f2fefe3bfc3b7edfaeef\ | ||
a7e3e1bff7f331e69fe5bfc3b7e3fdfbfedfdf5ff9fbfa7dbf5ddf4fafc3f1bff7f6\ | ||
fd777d3e1f8dffbf97c7fbf7fdbf5f4eef87e37fcbedfaeefa7c3f1bff7f2fb777e3\ | ||
7fefe5f2fefe3bfc3b7edfaeefa7e3e1bff7f331e69f7efdfbf7efdfbf7efdfbf7ef\ | ||
dfbf7efdfbf7efdfbf7efdfbf7efdfbf7efdfbf7efdfbf7efdfbf7efdfbf7efdfbf7\ | ||
efdfbf7efdfbf7efdfbf7efdfbf7efdfbcc79a7e8d11e72a321b66a4a5eae8e62f82\ | ||
9acb4d' | ||
}; | ||
@@ -204,5 +206,5 @@ | ||
value: -1, | ||
index: false | ||
index: true | ||
}, | ||
buffer: new Buffer('80', 'hex') | ||
buffer: new Buffer('8080', 'hex') | ||
}]; | ||
@@ -265,3 +267,3 @@ | ||
it('should return the Huffman encoded version of the input buffer', function() { | ||
var table = HuffmanTable.requestHuffmanTable; | ||
var table = HuffmanTable.huffmanTable; | ||
for (var decoded in test_huffman_request) { | ||
@@ -271,3 +273,3 @@ var encoded = test_huffman_request[decoded]; | ||
} | ||
table = HuffmanTable.responseHuffmanTable; | ||
table = HuffmanTable.huffmanTable; | ||
for (decoded in test_huffman_response) { | ||
@@ -281,3 +283,3 @@ encoded = test_huffman_response[decoded]; | ||
it('should return the Huffman decoded version of the input buffer', function() { | ||
var table = HuffmanTable.requestHuffmanTable; | ||
var table = HuffmanTable.huffmanTable; | ||
for (var decoded in test_huffman_request) { | ||
@@ -287,3 +289,3 @@ var encoded = test_huffman_request[decoded]; | ||
} | ||
table = HuffmanTable.responseHuffmanTable; | ||
table = HuffmanTable.huffmanTable; | ||
for (decoded in test_huffman_response) { | ||
@@ -309,3 +311,3 @@ encoded = test_huffman_response[decoded]; | ||
it('should return an array of buffers that represent the encoded form of the string', function() { | ||
var table = HuffmanTable.requestHuffmanTable; | ||
var table = HuffmanTable.huffmanTable; | ||
for (var i = 0; i < test_strings.length; i++) { | ||
@@ -319,3 +321,3 @@ var test = test_strings[i]; | ||
it('should return an array of buffers that represent the encoded form of the header', function() { | ||
var table = HuffmanTable.requestHuffmanTable; | ||
var table = HuffmanTable.huffmanTable; | ||
for (var i = 0; i < test_headers.length; i++) { | ||
@@ -342,3 +344,3 @@ var test = test_headers[i]; | ||
it('should return the parsed string and increase the cursor property of buffer', function() { | ||
var table = HuffmanTable.requestHuffmanTable; | ||
var table = HuffmanTable.huffmanTable; | ||
for (var i = 0; i < test_strings.length; i++) { | ||
@@ -354,3 +356,3 @@ var test = test_strings[i]; | ||
it('should return the parsed header and increase the cursor property of buffer', function() { | ||
var table = HuffmanTable.requestHuffmanTable; | ||
var table = HuffmanTable.huffmanTable; | ||
for (var i = 0; i < test_headers.length; i++) { | ||
@@ -443,3 +445,3 @@ var test = test_headers[i]; | ||
buffer = new Buffer(buffer); | ||
var table = HuffmanTable.requestHuffmanTable; | ||
var table = HuffmanTable.huffmanTable; | ||
var result = table.decode(table.encode(buffer)); | ||
@@ -446,0 +448,0 @@ expect(result).to.deep.equal(buffer); |
@@ -66,43 +66,2 @@ var expect = require('chai').expect; | ||
describe('invalid operation', function() { | ||
describe('disabling and the re-enabling flow control', function() { | ||
it('should result in an error event with type "FLOW_CONTROL_ERROR"', function(done) { | ||
var connection = new Connection(util.log, 1, settings); | ||
connection.on('error', function(error) { | ||
expect(error).to.equal('FLOW_CONTROL_ERROR'); | ||
done(); | ||
}); | ||
connection._setLocalFlowControl(true); | ||
connection._setLocalFlowControl(false); | ||
}); | ||
}); | ||
describe('manipulating flow control window after flow control was turned off', function() { | ||
it('should result in an error event with type "FLOW_CONTROL_ERROR"', function(done) { | ||
var connection = new Connection(util.log, 1, settings); | ||
connection.on('error', function(error) { | ||
expect(error).to.equal('FLOW_CONTROL_ERROR'); | ||
done(); | ||
}); | ||
connection._setLocalFlowControl(true); | ||
connection._setInitialStreamWindowSize(10); | ||
}); | ||
}); | ||
describe('disabling flow control twice', function() { | ||
it('should be ignored', function() { | ||
var connection = new Connection(util.log, 1, settings); | ||
connection._setLocalFlowControl(true); | ||
connection._setLocalFlowControl(true); | ||
}); | ||
}); | ||
describe('enabling flow control when already enabled', function() { | ||
it('should be ignored', function() { | ||
var connection = new Connection(util.log, 1, settings); | ||
connection._setLocalFlowControl(false); | ||
}); | ||
}); | ||
describe('unsolicited ping answer', function() { | ||
@@ -109,0 +68,0 @@ it('should be ignored', function() { |
@@ -85,9 +85,2 @@ var expect = require('chai').expect; | ||
}); | ||
describe('.disableLocalFlowControl() method', function() { | ||
it('should increase `this._window` by Infinity', function() { | ||
flow._send = util.noop; | ||
flow.disableLocalFlowControl(); | ||
expect(flow._window).to.equal(Infinity); | ||
}); | ||
}); | ||
describe('.read() method', function() { | ||
@@ -136,3 +129,2 @@ describe('when the flow control queue is not empty', function() { | ||
'disabled', function(done) { | ||
flow._remoteFlowControlDisabled = false; | ||
flow._window = 100; | ||
@@ -164,3 +156,2 @@ flow._send = util.noop; | ||
flow1._flowControlId = flow2._flowControlId; | ||
flow1._remoteFlowControlDisabled = flow2._remoteFlowControlDisabled = false; | ||
flow1._send = flow2._send = util.noop; | ||
@@ -167,0 +158,0 @@ flow1._receive = flow2._receive = function(frame, callback) { callback(); }; |
@@ -24,3 +24,4 @@ var expect = require('chai').expect; | ||
type: 'DATA', | ||
flags: { END_STREAM: false, RESERVED: false }, | ||
flags: { END_STREAM: false, END_SEGMENT: false, RESERVED4: false, | ||
RESERVED8: false, PAD_LOW: false, PAD_HIGH: false }, | ||
stream: 10, | ||
@@ -36,3 +37,4 @@ | ||
type: 'HEADERS', | ||
flags: { END_STREAM: false, RESERVED: false, END_HEADERS: false, PRIORITY: false }, | ||
flags: { END_STREAM: false, END_SEGMENT: false, END_HEADERS: false, | ||
PRIORITY: false, PAD_LOW: false, PAD_HIGH: false }, | ||
stream: 15, | ||
@@ -47,3 +49,4 @@ | ||
type: 'HEADERS', | ||
flags: { END_STREAM: false, RESERVED: false, END_HEADERS: false, PRIORITY: true }, | ||
flags: { END_STREAM: false, END_SEGMENT: false, END_HEADERS: false, | ||
PRIORITY: true, PAD_LOW: false, PAD_HIGH: false }, | ||
stream: 15, | ||
@@ -86,11 +89,9 @@ | ||
SETTINGS_MAX_CONCURRENT_STREAMS: 0x01234567, | ||
SETTINGS_INITIAL_WINDOW_SIZE: 0x89ABCDEF, | ||
SETTINGS_FLOW_CONTROL_OPTIONS: true | ||
SETTINGS_INITIAL_WINDOW_SIZE: 0x89ABCDEF | ||
} | ||
}, | ||
buffer: new Buffer('0028' + '04' + '00' + '0000000A' + '00' + '000001' + '12345678' + | ||
'00' + '000002' + '00000001' + | ||
'00' + '000004' + '01234567' + | ||
'00' + '000007' + '89ABCDEF' + | ||
'00' + '00000A' + '00000001', 'hex') | ||
buffer: new Buffer('0014' + '04' + '00' + '0000000A' + '01' + '12345678' + | ||
'02' + '00000001' + | ||
'03' + '01234567' + | ||
'04' + '89ABCDEF', 'hex') | ||
@@ -137,7 +138,8 @@ }, { | ||
}, | ||
buffer: new Buffer('0004' + '09' + '00' + '0000000A' + '12345678', 'hex') | ||
buffer: new Buffer('0004' + '08' + '00' + '0000000A' + '12345678', 'hex') | ||
}, { | ||
frame: { | ||
type: 'CONTINUATION', | ||
flags: { RESERVED1: false, RESERVED2: false, END_HEADERS: true }, | ||
flags: { RESERVED1: false, RESERVED2: false, END_HEADERS: true, | ||
RESERVED8: false, PAD_LOW: false, PAD_HIGH: false }, | ||
stream: 10, | ||
@@ -148,5 +150,57 @@ | ||
// length + type + flags + stream + content | ||
buffer: new Buffer('0004' + '0A' + '04' + '0000000A' + '12345678', 'hex') | ||
buffer: new Buffer('0004' + '09' + '04' + '0000000A' + '12345678', 'hex') | ||
}]; | ||
var deserializer_test_frames = test_frames.slice(0); | ||
var padded_test_frames = [{ | ||
frame: { | ||
type: 'DATA', | ||
flags: { END_STREAM: false, END_SEGMENT: false, RESERVED4: false, | ||
RESERVED8: false, PAD_LOW: true, PAD_HIGH: false }, | ||
stream: 10, | ||
data: new Buffer('12345678', 'hex') | ||
}, | ||
// length + type + flags + stream + pad_low control + content + padding | ||
buffer: new Buffer('000B' + '00' + '10' + '0000000A' + '06' + '12345678' + '000000000000', 'hex') | ||
}, { | ||
frame: { | ||
type: 'HEADERS', | ||
flags: { END_STREAM: false, END_SEGMENT: false, END_HEADERS: false, | ||
PRIORITY: false, PAD_LOW: true, PAD_HIGH: false }, | ||
stream: 15, | ||
data: new Buffer('12345678', 'hex') | ||
}, | ||
buffer: new Buffer('000B' + '01' + '10' + '0000000F' + '06' + '12345678' + '000000000000', 'hex') | ||
}, { | ||
frame: { | ||
type: 'HEADERS', | ||
flags: { END_STREAM: false, END_SEGMENT: false, END_HEADERS: false, | ||
PRIORITY: true, PAD_LOW: true, PAD_HIGH: false }, | ||
stream: 15, | ||
priority: 3, | ||
data: new Buffer('12345678', 'hex') | ||
}, | ||
buffer: new Buffer('000F' + '01' + '18' + '0000000F' + '06' + '00000003' + '12345678' + '000000000000', 'hex') | ||
}, { | ||
frame: { | ||
type: 'CONTINUATION', | ||
flags: { RESERVED1: false, RESERVED2: false, END_HEADERS: true, | ||
RESERVED8: false, PAD_LOW: true, PAD_HIGH: false }, | ||
stream: 10, | ||
data: new Buffer('12345678', 'hex') | ||
}, | ||
// length + type + flags + stream + content | ||
buffer: new Buffer('000B' + '09' + '14' + '0000000A' + '06' + '12345678' + '000000000000', 'hex') | ||
}]; | ||
for (var idx = 0; idx < padded_test_frames.length; idx++) { | ||
deserializer_test_frames.push(padded_test_frames[idx]); | ||
} | ||
describe('framer.js', function() { | ||
@@ -201,4 +255,4 @@ describe('Serializer', function() { | ||
it('should augment the frame object with these properties: { type, flags, stream })', function() { | ||
for (var i = 0; i < test_frames.length; i++) { | ||
var test = test_frames[i], frame = {}; | ||
for (var i = 0; i < deserializer_test_frames.length; i++) { | ||
var test = deserializer_test_frames[i], frame = {}; | ||
Deserializer.commonHeader(test.buffer.slice(0,8), frame); | ||
@@ -215,3 +269,3 @@ expect(frame).to.deep.equal({ | ||
Object.keys(frame_types).forEach(function(type) { | ||
var tests = test_frames.filter(function(test) { return test.frame.type === type; }); | ||
var tests = deserializer_test_frames.filter(function(test) { return test.frame.type === type; }); | ||
var frame_shape = '{ ' + frame_types[type].join(', ') + ' }'; | ||
@@ -238,7 +292,7 @@ describe('static method .' + type + '(payload_buffer, frame)', function() { | ||
var shuffled = util.shuffleBuffers(test_frames.map(function(test) { return test.buffer; })); | ||
var shuffled = util.shuffleBuffers(deserializer_test_frames.map(function(test) { return test.buffer; })); | ||
shuffled.forEach(stream.write.bind(stream)); | ||
for (var j = 0; j < test_frames.length; j++) { | ||
expect(stream.read()).to.be.deep.equal(test_frames[j].frame); | ||
for (var j = 0; j < deserializer_test_frames.length; j++) { | ||
expect(stream.read()).to.be.deep.equal(deserializer_test_frames[j].frame); | ||
} | ||
@@ -245,0 +299,0 @@ }); |
@@ -10,3 +10,2 @@ var expect = require('chai').expect; | ||
stream.upstream._window = Infinity; | ||
stream.upstream._remoteFlowControlDisabled = true; | ||
return stream; | ||
@@ -189,4 +188,7 @@ } | ||
var stream = createStream(); | ||
var connectionErrorHappened = false | ||
stream.state = state; | ||
expect(stream._transition.bind(stream, false, invalid_frame)).to.throw('Uncaught, unspecified "error" event.'); | ||
stream.once('connectionError', function() { connectionErrorHappened = true; }); | ||
stream._transition(false, invalid_frame) | ||
expect(connectionErrorHappened) | ||
}); | ||
@@ -193,0 +195,0 @@ }); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
23
86
230319
5338