Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release proposal: v3.3.2 (final io.js v3.x) #3465

Closed
wants to merge 4 commits into from
Closed
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
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,30 @@
# io.js ChangeLog

## 2015-10-21, Version 3.3.2, @rvagg

Security release for v3.x, likely the final v3.x release for io.js. Please migrate to Node.js v4+.

* http:
- Fix out-of-order 'finish' event bug in pipelining that can abort execution, fixes DoS vulnerability CVE-2015-7384 (Fedor Indutny) #3128
- Account for pending response data instead of just the data on the current request to decide whether pause the socket or not (Fedor Indutny) #3128

### Known issues

See https://github.com/nodejs/io.js/labels/confirmed-bug for complete and current list of known issues.

* Some uses of computed object shorthand properties are not handled correctly by the current version of V8. e.g. `[{ [prop]: val }]` evaluates to `[{}]`. [#2507](https://github.com/nodejs/node/issues/2507)
* Some problems with unreferenced timers running during `beforeExit` are still to be resolved. See [#1264](https://github.com/nodejs/io.js/issues/1264).
* Surrogate pair in REPL can freeze terminal. [#690](https://github.com/nodejs/io.js/issues/690)
* `process.send()` is not synchronous as the docs suggest, a regression introduced in 1.0.2, see [#760](https://github.com/nodejs/io.js/issues/760).
* Calling `dns.setServers()` while a DNS query is in progress can cause the process to crash on a failed assertion. [#894](https://github.com/nodejs/io.js/issues/894)
* `url.resolve` may transfer the auth portion of the url when resolving between two full hosts, see [#1435](https://github.com/nodejs/io.js/issues/1435).

### Commits

* [[`eb0d901cb9`](https://github.com/nodejs/node/commit/eb0d901cb9)] - **http**: fix out-of-order 'finish' bug in pipelining (Fedor Indutny) [#3128](https://github.com/nodejs/node/pull/3128)
* [[`006484857d`](https://github.com/nodejs/node/commit/006484857d)] - **http_server**: pause socket properly (Fedor Indutny) [#3128](https://github.com/nodejs/node/pull/3128)
* [[`8b54f401ad`](https://github.com/nodejs/node/commit/8b54f401ad)] - **stream_wrap**: support empty `TryWrite`s (Fedor Indutny) [#3128](https://github.com/nodejs/node/pull/3128)

## 2015-09-15, Version 3.3.1, @rvagg

### Notable changes
Expand Down
22 changes: 16 additions & 6 deletions lib/_http_outgoing.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ function OutgoingMessage() {
this.output = [];
this.outputEncodings = [];
this.outputCallbacks = [];
this.outputSize = 0;

this.writable = true;

Expand All @@ -71,6 +72,8 @@ function OutgoingMessage() {
this._header = null;
this._headers = null;
this._headerNames = {};

this._onPendingData = null;
}
util.inherits(OutgoingMessage, Stream);

Expand Down Expand Up @@ -120,6 +123,9 @@ OutgoingMessage.prototype._send = function(data, encoding, callback) {
this.output.unshift(this._header);
this.outputEncodings.unshift('binary');
this.outputCallbacks.unshift(null);
this.outputSize += this._header.length;
if (this._onPendingData !== null)
this._onPendingData(this._header.length);
}
this._headerSent = true;
}
Expand All @@ -133,12 +139,6 @@ OutgoingMessage.prototype._writeRaw = function(data, encoding, callback) {
encoding = null;
}

if (data.length === 0) {
if (typeof callback === 'function')
process.nextTick(callback);
return true;
}

var connection = this.connection;
if (connection &&
connection._httpMessage === this &&
Expand All @@ -158,6 +158,13 @@ OutgoingMessage.prototype._writeRaw = function(data, encoding, callback) {
this.output = [];
this.outputEncodings = [];
this.outputCallbacks = [];
if (this._onPendingData !== null)
this._onPendingData(-this.outputSize);
this.outputSize = 0;
} else if (data.length === 0) {
if (typeof callback === 'function')
process.nextTick(callback);
return true;
}

// Directly write to socket.
Expand All @@ -177,6 +184,9 @@ OutgoingMessage.prototype._buffer = function(data, encoding, callback) {
this.output.push(data);
this.outputEncodings.push(encoding);
this.outputCallbacks.push(callback);
this.outputSize += data.length;
if (this._onPendingData !== null)
this._onPendingData(data.length);
return false;
};

Expand Down
17 changes: 15 additions & 2 deletions lib/_http_server.js
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,8 @@ function Server(requestListener) {
});

this.timeout = 2 * 60 * 1000;

this._pendingResponseData = 0;
}
util.inherits(Server, net.Server);

Expand All @@ -259,6 +261,13 @@ function connectionListener(socket) {
var self = this;
var outgoing = [];
var incoming = [];
var outgoingData = 0;

function updateOutgoingData(delta) {
outgoingData += delta;
if (socket._paused && outgoingData < socket._writableState.highWaterMark)
return socketOnDrain();
}

function abortIncoming() {
while (incoming.length) {
Expand Down Expand Up @@ -424,8 +433,10 @@ function connectionListener(socket) {

socket._paused = false;
function socketOnDrain() {
var needPause = outgoingData > socket._writableState.highWaterMark;

// If we previously paused, then start reading again.
if (socket._paused) {
if (socket._paused && !needPause) {
socket._paused = false;
socket.parser.resume();
socket.resume();
Expand All @@ -439,7 +450,8 @@ function connectionListener(socket) {
// so that we don't become overwhelmed by a flood of
// pipelined requests that may never be resolved.
if (!socket._paused) {
var needPause = socket._writableState.needDrain;
var needPause = socket._writableState.needDrain ||
outgoingData >= socket._writableState.highWaterMark;
if (needPause) {
socket._paused = true;
// We also need to pause the parser, but don't do that until after
Expand All @@ -450,6 +462,7 @@ function connectionListener(socket) {
}

var res = new ServerResponse(req);
res._onPendingData = updateOutgoingData;

res.shouldKeepAlive = shouldKeepAlive;
DTRACE_HTTP_SERVER_REQUEST(req, socket);
Expand Down
2 changes: 1 addition & 1 deletion src/node_version.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#define NODE_MINOR_VERSION 3
#define NODE_PATCH_VERSION 2

#define NODE_VERSION_IS_RELEASE 0
#define NODE_VERSION_IS_RELEASE 1

#ifndef NODE_STRINGIFY
#define NODE_STRINGIFY(n) NODE_STRINGIFY_HELPER(n)
Expand Down
2 changes: 1 addition & 1 deletion src/stream_base.cc
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ int StreamBase::Writev(const FunctionCallbackInfo<Value>& args) {

// Write string
offset = ROUND_UP(offset, WriteWrap::kAlignSize);
CHECK_LT(offset, storage_size);
CHECK_LE(offset, storage_size);
char* str_storage = req_wrap->Extra(offset);
size_t str_size = storage_size - offset;

Expand Down
2 changes: 1 addition & 1 deletion src/stream_wrap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ int StreamWrap::DoTryWrite(uv_buf_t** bufs, size_t* count) {
// Slice off the buffers: skip all written buffers and slice the one that
// was partially written.
written = err;
for (; written != 0 && vcount > 0; vbufs++, vcount--) {
for (; vcount > 0; vbufs++, vcount--) {
// Slice
if (vbufs[0].len > written) {
vbufs[0].base += written;
Expand Down
34 changes: 34 additions & 0 deletions test/parallel/test-http-pipeline-regr-2639.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const http = require('http');
const net = require('net');

const COUNT = 10;

var received = 0;

var server = http.createServer(function(req, res) {
// Close the server, we have only one TCP connection anyway
if (received++ === 0)
server.close();

res.writeHead(200);
res.write('data');

setTimeout(function() {
res.end();
}, (Math.random() * 100) | 0);
}).listen(common.PORT, function() {
const s = net.connect(common.PORT);

var big = '';
for (var i = 0; i < COUNT; i++)
big += 'GET / HTTP/1.0\r\n\r\n';
s.write(big);
s.resume();
});

process.on('exit', function() {
assert.equal(received, COUNT);
});