Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
Version history
===============

### 3.3.8 (2018-02-15) ###
* Fix an issue with HTTP trailers and END_STREAM.

### 3.3.7 (2017-09-21) ###
* Mark as incompatible with node >= 9.0.0 (to encourage using the built-in http2 module available by default in node >= 9.0.0).

### 3.3.6 (2016-09-16) ###
* We were not appropriately sending HPACK context updates when receiving SETTINGS_HEADER_TABLE_SIZE. This release fixes that bug.

Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ client and server implementation for node.js.

![Travis CI status](https://travis-ci.org/molnarg/node-http2.svg?branch=master)

**NOTE WELL** This package is officially deprecated. As of node 9.0.0, there is an 'http2' package built-in. You should use that one instead.

Installation
------------

Expand Down
2 changes: 1 addition & 1 deletion lib/http.js
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ OutgoingMessage.prototype._finish = function _finish() {
if (this.request) {
this.request.addTrailers(this._trailers);
} else {
this.stream.headers(this._trailers);
this.stream.trailers(this._trailers);
}
}
this.finished = true;
Expand Down
13 changes: 12 additions & 1 deletion lib/protocol/connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ Connection.prototype._initializeStreamManagement = function _initializeStreamMan
Connection.prototype._writeControlFrame = function _writeControlFrame(frame) {
if ((frame.type === 'SETTINGS') || (frame.type === 'PING') ||
(frame.type === 'GOAWAY') || (frame.type === 'WINDOW_UPDATE') ||
(frame.type === 'ALTSVC')) {
(frame.type === 'ALTSVC') || (frame.type == 'ORIGIN')) {
this._log.debug({ frame: frame }, 'Receiving connection level frame');
this.emit(frame.type, frame);
} else {
Expand Down Expand Up @@ -557,6 +557,17 @@ Connection.prototype._receivePing = function _receivePing(frame) {
}
};

Connection.prototype.originFrame = function originFrame(originList) {
this._log.debug(originList, 'emitting origin frame');

this.push({
type: 'ORIGIN',
flags: {},
stream: 0,
originList : originList,
});
};

// Terminating the connection
Connection.prototype.close = function close(error) {
if (this._closed) {
Expand Down
10 changes: 1 addition & 9 deletions lib/protocol/flow.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ function Flow(flowControlId) {
this._queue = [];
this._ended = false;
this._received = 0;
this._blocked = false;
}
Flow.prototype = Object.create(Duplex.prototype, { constructor: { value: Flow } });

Expand Down Expand Up @@ -157,7 +156,6 @@ Flow.prototype._read = function _read() {
// * if there are items in the flow control queue, then let's put them into the output queue (to
// the extent it is possible with respect to the window size and output queue feedback)
else if (this._window > 0) {
this._blocked = false;
this._readableState.sync = true; // to avoid reentrant calls
do {
var moreNeeded = this._push(this._queue[0]);
Expand All @@ -173,14 +171,8 @@ Flow.prototype._read = function _read() {
}

// * otherwise, come back when the flow control window is positive
else if (!this._blocked) {
this._parentPush({
type: 'BLOCKED',
flags: {},
stream: this._flowControlId
});
else {
this.once('window_update', this._read);
this._blocked = true;
}
};

Expand Down
29 changes: 15 additions & 14 deletions lib/protocol/framer.js
Original file line number Diff line number Diff line change
Expand Up @@ -1069,26 +1069,27 @@ Deserializer.ALTSVC = function readAltSvc(buffer, frame) {
}
};

// BLOCKED
// ------------------------------------------------------------
//
// The BLOCKED frame (type=0xB) indicates that the sender is unable to send data
// due to a closed flow control window.
//
// The BLOCKED frame does not define any flags and contains no payload.

frameTypes[0xB] = 'BLOCKED';
// frame 0xB was BLOCKED and some versions of chrome will
// throw PROTOCOL_ERROR upon seeing it with non 0 payload

frameFlags.BLOCKED = [];
frameTypes[0xC] = 'ORIGIN';
frameFlags.ORIGIN = [];
typeSpecificAttributes.ORIGIN = ['originList'];

typeSpecificAttributes.BLOCKED = [];

Serializer.BLOCKED = function writeBlocked(frame, buffers) {
Serializer.ORIGIN = function writeOrigin(frame, buffers) {
for (var i = 0; i < frame.originList.length; i++) {
var buffer = new Buffer(2);
buffer.writeUInt16BE(frame.originList[i].length, 0);
buffers.push(buffer);
buffers.push(new Buffer(frame.originList[i], 'ascii'));
}
};

Deserializer.BLOCKED = function readBlocked(buffer, frame) {
Deserializer.ORIGIN = function readOrigin(buffer, frame) {
// ignored
};


// [Error Codes](https://tools.ietf.org/html/rfc7540#section-7)
// ------------------------------------------------------------

Expand Down
32 changes: 25 additions & 7 deletions lib/protocol/stream.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ function Stream(log, connection) {
this._initializeState();

this.connection = connection;
this.sentEndStream = false;
}

Stream.prototype = Object.create(Duplex.prototype, { constructor: { value: Stream } });
Expand Down Expand Up @@ -106,6 +107,16 @@ Stream.prototype.headers = function headers(headers) {
});
};

Stream.prototype.trailers = function trailers(trailers) {
this.sentEndStream = true;
this._pushUpstream({
type: 'HEADERS',
flags: {'END_STREAM': true},
stream: this.id,
headers: trailers
});
};

Stream.prototype._onHeaders = function _onHeaders(frame) {
if (frame.priority !== undefined) {
this.priority(frame.priority, true);
Expand Down Expand Up @@ -253,7 +264,7 @@ Stream.prototype._writeUpstream = function _writeUpstream(frame) {
this._onPriority(frame);
} else if (frame.type === 'ALTSVC') {
// TODO
} else if (frame.type === 'BLOCKED') {
} else if (frame.type === 'ORIGIN') {
// TODO
}

Expand Down Expand Up @@ -342,6 +353,13 @@ Stream.prototype._finishing = function _finishing() {
stream: this.id,
data: emptyBuffer
};

if (this.sentEndStream) {
this._log.debug('Already sent END_STREAM, not sending again.');
return;
}

this.sentEndStream = true;
var lastFrame = this.upstream.getLastQueuedFrame();
if (lastFrame && ((lastFrame.type === 'DATA') || (lastFrame.type === 'HEADERS'))) {
this._log.debug({ frame: lastFrame }, 'Marking last frame with END_STREAM flag.');
Expand Down Expand Up @@ -413,7 +431,7 @@ Stream.prototype._transition = function transition(sending, frame) {
var connectionError;
var streamError;

var DATA = false, HEADERS = false, PRIORITY = false, ALTSVC = false, BLOCKED = false;
var DATA = false, HEADERS = false, PRIORITY = false, ALTSVC = false, ORIGIN = false;
var RST_STREAM = false, PUSH_PROMISE = false, WINDOW_UPDATE = false;
switch(frame.type) {
case 'DATA' : DATA = true; break;
Expand All @@ -423,7 +441,7 @@ Stream.prototype._transition = function transition(sending, frame) {
case 'PUSH_PROMISE' : PUSH_PROMISE = true; break;
case 'WINDOW_UPDATE': WINDOW_UPDATE = true; break;
case 'ALTSVC' : ALTSVC = true; break;
case 'BLOCKED' : BLOCKED = true; break;
case 'ORIGIN' : ORIGIN = true; break;
}

var previousState = this.state;
Expand Down Expand Up @@ -483,7 +501,7 @@ Stream.prototype._transition = function transition(sending, frame) {
this._setState('CLOSED');
} else if (receiving && HEADERS) {
this._setState('HALF_CLOSED_LOCAL');
} else if (BLOCKED || PRIORITY) {
} else if (PRIORITY || ORIGIN) {
/* No state change */
} else {
connectionError = 'PROTOCOL_ERROR';
Expand Down Expand Up @@ -518,7 +536,7 @@ Stream.prototype._transition = function transition(sending, frame) {
case 'HALF_CLOSED_LOCAL':
if (RST_STREAM || (receiving && frame.flags.END_STREAM)) {
this._setState('CLOSED');
} else if (BLOCKED || ALTSVC || receiving || PRIORITY || (sending && WINDOW_UPDATE)) {
} else if (ORIGIN || ALTSVC || receiving || PRIORITY || (sending && WINDOW_UPDATE)) {
/* No state change */
} else {
connectionError = 'PROTOCOL_ERROR';
Expand All @@ -538,7 +556,7 @@ Stream.prototype._transition = function transition(sending, frame) {
case 'HALF_CLOSED_REMOTE':
if (RST_STREAM || (sending && frame.flags.END_STREAM)) {
this._setState('CLOSED');
} else if (BLOCKED || ALTSVC || sending || PRIORITY || (receiving && WINDOW_UPDATE)) {
} else if (ORIGIN || ALTSVC || sending || PRIORITY || (receiving && WINDOW_UPDATE)) {
/* No state change */
} else {
connectionError = 'PROTOCOL_ERROR';
Expand Down Expand Up @@ -569,7 +587,7 @@ Stream.prototype._transition = function transition(sending, frame) {
if (PRIORITY || (sending && RST_STREAM) ||
(receiving && WINDOW_UPDATE) ||
(receiving && this._closedByUs &&
(this._closedWithRst || RST_STREAM || ALTSVC))) {
(this._closedWithRst || RST_STREAM || ALTSVC || ORIGIN))) {
/* No state change */
} else {
streamError = 'STREAM_CLOSED';
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"name": "http2",
"version": "3.3.6",
"version": "3.3.8",
"description": "An HTTP/2 client and server implementation",
"main": "lib/index.js",
"engines" : {
"node" : ">=0.12.0"
"engines": {
"node": ">=0.12.0 <9.0.0"
},
"devDependencies": {
"istanbul": "*",
Expand Down