diff --git a/CHANGELOG.md b/CHANGELOG.md index 007850e33029b2..46dd050785e7e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/lib/_http_outgoing.js b/lib/_http_outgoing.js index c9b0a87b3dbd0c..c550bcf14635c0 100644 --- a/lib/_http_outgoing.js +++ b/lib/_http_outgoing.js @@ -49,6 +49,7 @@ function OutgoingMessage() { this.output = []; this.outputEncodings = []; this.outputCallbacks = []; + this.outputSize = 0; this.writable = true; @@ -71,6 +72,8 @@ function OutgoingMessage() { this._header = null; this._headers = null; this._headerNames = {}; + + this._onPendingData = null; } util.inherits(OutgoingMessage, Stream); @@ -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; } @@ -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 && @@ -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. @@ -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; }; diff --git a/lib/_http_server.js b/lib/_http_server.js index 7acc1086387880..5dcea3d87fe2c4 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -240,6 +240,8 @@ function Server(requestListener) { }); this.timeout = 2 * 60 * 1000; + + this._pendingResponseData = 0; } util.inherits(Server, net.Server); @@ -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) { @@ -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(); @@ -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 @@ -450,6 +462,7 @@ function connectionListener(socket) { } var res = new ServerResponse(req); + res._onPendingData = updateOutgoingData; res.shouldKeepAlive = shouldKeepAlive; DTRACE_HTTP_SERVER_REQUEST(req, socket); diff --git a/src/node_version.h b/src/node_version.h index cc86ce970a3ad7..dd14343cf338ea 100644 --- a/src/node_version.h +++ b/src/node_version.h @@ -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) diff --git a/src/stream_base.cc b/src/stream_base.cc index d957465abcdcbb..a83df55e0a206c 100644 --- a/src/stream_base.cc +++ b/src/stream_base.cc @@ -155,7 +155,7 @@ int StreamBase::Writev(const FunctionCallbackInfo& 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; diff --git a/src/stream_wrap.cc b/src/stream_wrap.cc index ec98acbb30bb4a..03ca9e04e3a754 100644 --- a/src/stream_wrap.cc +++ b/src/stream_wrap.cc @@ -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; diff --git a/test/parallel/test-http-pipeline-regr-2639.js b/test/parallel/test-http-pipeline-regr-2639.js new file mode 100644 index 00000000000000..f1f75bff650ac5 --- /dev/null +++ b/test/parallel/test-http-pipeline-regr-2639.js @@ -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); +});