diff --git a/AUTHORS b/AUTHORS index a46220775054f6..cd2b3aa983003f 100644 --- a/AUTHORS +++ b/AUTHORS @@ -744,5 +744,8 @@ Giovanny Andres Gongora Granada Jeffrey Jagoda Kelsey Breseman Peter Petrov +Andrew Crites +Marat Abdullin +Dan Varga # Generated by tools/update-authors.sh diff --git a/COLLABORATOR_GUIDE.md b/COLLABORATOR_GUIDE.md index 20949eeefbab5b..792dbf3d4d668a 100644 --- a/COLLABORATOR_GUIDE.md +++ b/COLLABORATOR_GUIDE.md @@ -119,14 +119,14 @@ $ git rebase --abort Checkout proper target branch ```text -$ git checkout v1.x +$ git checkout master ``` Update the tree ```text $ git fetch origin -$ git merge --ff-only origin/v1.x +$ git merge --ff-only origin/master ``` Apply external patches @@ -138,13 +138,13 @@ $ curl -L https://github.com/iojs/io.js/pull/xxx.patch | git am --whitespace=fix Check and re-review the changes ```text -$ git diff origin/v1.x +$ git diff origin/master ``` Check number of commits and commit messages ```text -$ git log origin/v1.x...v1.x +$ git log origin/master...master ``` If there are multiple commits that relate to the same feature or @@ -152,7 +152,7 @@ one with a feature and separate with a test for that feature - you'll need to squash them (or strictly speaking `fixup`). ```text -$ git rebase -i origin/v1.x +$ git rebase -i origin/master ``` This will open a screen like this (in the default shell editor): @@ -210,7 +210,7 @@ line. Time to push it: ```text -$ git push origin v1.x +$ git push origin master ``` ### I just made a mistake diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dfb68c43c6d566..d2edd60a0ee245 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -34,13 +34,8 @@ $ git remote add upstream git://github.com/iojs/io.js.git #### Which branch? -Now decide if you want your feature or bug fix to go into the master branch -or the stable branch. As a rule of thumb, bug fixes go into the stable branch -while new features go into the master branch. - -The stable branch is effectively frozen; patches that change the io.js -API/ABI or affect the run-time behavior of applications get rejected. The -current stable branch is set as the default branch on GitHub. +For developing new features and bug fixes, the `master` branch should be pulled +and built upon. #### Respect the stability index @@ -49,7 +44,7 @@ The rules for the master branch are less strict; consult the In a nutshell, modules are at varying levels of API stability. Bug fixes are always welcome but API or behavioral changes to modules at stability level 3 -and up are off-limits. +(Locked) are off-limits. #### Dependencies @@ -71,12 +66,9 @@ does not align with the project team. Create a feature branch and start hacking: ```text -$ git checkout -b my-feature-branch -t origin/v1.x +$ git checkout -b my-feature-branch -t origin/master ``` -(Where `v1.x` is the latest stable branch as of this writing.) - - ### Step 3: Commit Make sure git knows your name and email address: @@ -123,7 +115,7 @@ Use `git rebase` (not `git merge`) to sync your work from time to time. ```text $ git fetch upstream -$ git rebase upstream/v1.x # or upstream/master +$ git rebase upstream/master ``` @@ -147,10 +139,10 @@ can use this syntax to run it exactly as the test harness would: $ python tools/test.py -v --mode=release parallel/test-stream2-transform ``` -You can run tests directly with node: +You can run tests directly with iojs: ```text -$ node ./test/parallel/test-streams2-transform.js +$ iojs ./test/parallel/test-streams2-transform.js ``` diff --git a/README.md b/README.md index 67164f5725de54..ca5ab327b8ba39 100644 --- a/README.md +++ b/README.md @@ -280,7 +280,7 @@ information about the governance of the io.js project, see - Release GPG key: DD8F2338BAE7501E3DD5AC78C273792F7D83545D * **Thorsten Lorenz** ([@thlorenz](https://github.com/thlorenz)) <thlorenz@gmx.de> * **Stephen Belanger** ([@qard](https://github.com/qard)) <admin@stephenbelanger.com> -* **Jeremiah Senkpiel** ([@fishrock123](https://github.com/fishrock123)) <fishrock123@rocketmail.com> +* **Jeremiah Senkpiel** ([@fishrock123](https://github.com/fishrock123)) <fishrock123@rocketmail.com> (Technical Committee) - Release GPG key: FD3A5288F042B6850C66B31F09FE44734EB7990E * **Evan Lucas** ([@evanlucas](https://github.com/evanlucas)) <evanlucas@me.com> * **Brendan Ashworth** ([@brendanashworth](https://github.com/brendanashworth)) <brendan.ashworth@me.com> diff --git a/WORKING_GROUPS.md b/WORKING_GROUPS.md index 05cb41e5a97a82..c5c7cc1d746a50 100644 --- a/WORKING_GROUPS.md +++ b/WORKING_GROUPS.md @@ -1,7 +1,7 @@ # io.js Working Groups io.js Working Groups are autonomous projects created by the -[Technical Committee (TC)](https://github.com/iojs/io.js/blob/v1.x/GOVERNANCE.md#technical-committee). +[Technical Committee (TC)](https://github.com/iojs/io.js/blob/master/GOVERNANCE.md#technical-committee). Working Groups can be formed at any time but must be ratified by the TC. Once formed the work defined in the Working Group charter is the diff --git a/doc/api/child_process.markdown b/doc/api/child_process.markdown index 1ca5cd7fbac6ed..315670581bba4f 100644 --- a/doc/api/child_process.markdown +++ b/doc/api/child_process.markdown @@ -192,7 +192,7 @@ be sent `'SIGTERM'`. See `signal(7)` for a list of available signals. grep = spawn('grep', ['ssh']); grep.on('close', function (code, signal) { - console.log('child process terminated due to receipt of signal '+signal); + console.log('child process terminated due to receipt of signal ' + signal); }); // send SIGHUP to process diff --git a/doc/api/net.markdown b/doc/api/net.markdown index e3ce8fb5863aa0..aadf8a84c1c1a6 100644 --- a/doc/api/net.markdown +++ b/doc/api/net.markdown @@ -355,6 +355,8 @@ For TCP sockets, `options` argument should be an object which specifies: - `localPort`: Local port to bind to for network connections. - `family` : Version of IP stack. Defaults to `4`. + + - `lookup` : Custom lookup function. Defaults to `dns.lookup`. For local domain sockets, `options` argument should be an object which specifies: diff --git a/lib/_http_client.js b/lib/_http_client.js index daa37ef064e3fc..200a08e5d5bf85 100644 --- a/lib/_http_client.js +++ b/lib/_http_client.js @@ -21,6 +21,8 @@ function ClientRequest(options, cb) { if (typeof options === 'string') { options = url.parse(options); + } else { + options = util._extend({}, options); } var agent = options.agent; diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js index 3e091b0fc1be0d..c1037a7096a755 100644 --- a/lib/_tls_wrap.js +++ b/lib/_tls_wrap.js @@ -858,7 +858,8 @@ exports.connect = function(/* [port, host], options, cb */) { var hostname = options.servername || options.host || - options.socket && options.socket._host, + (options.socket && options.socket._host) || + 'localhost', NPN = {}, context = tls.createSecureContext(options); tls.convertNPNProtocols(options.NPNProtocols, NPN); diff --git a/lib/buffer.js b/lib/buffer.js index 8f4e34d289fc27..dc2b656d7a7a33 100644 --- a/lib/buffer.js +++ b/lib/buffer.js @@ -247,6 +247,11 @@ Buffer.concat = function(list, length) { if (!Array.isArray(list)) throw new TypeError('list argument must be an Array of Buffers.'); + if (list.length === 0) + return new Buffer(0); + else if (list.length === 1) + return list[0]; + if (length === undefined) { length = 0; for (var i = 0; i < list.length; i++) @@ -255,11 +260,6 @@ Buffer.concat = function(list, length) { length = length >>> 0; } - if (list.length === 0) - return new Buffer(0); - else if (list.length === 1) - return list[0]; - var buffer = new Buffer(length); var pos = 0; for (var i = 0; i < list.length; i++) { diff --git a/lib/module.js b/lib/module.js index 02f0ec700f150d..eba3de81713fe8 100644 --- a/lib/module.js +++ b/lib/module.js @@ -489,7 +489,9 @@ Module._initPaths = function() { var nodePath = process.env['NODE_PATH']; if (nodePath) { - paths = nodePath.split(path.delimiter).concat(paths); + paths = nodePath.split(path.delimiter).filter(function(path) { + return !!path; + }).concat(paths); } modulePaths = paths; diff --git a/lib/net.js b/lib/net.js index 847e417e67f4ca..7eb3c8ee72e705 100644 --- a/lib/net.js +++ b/lib/net.js @@ -319,14 +319,25 @@ Socket.prototype._onTimeout = function() { Socket.prototype.setNoDelay = function(enable) { + if (!this._handle) { + this.once('connect', + enable ? this.setNoDelay : this.setNoDelay.bind(this, enable)); + return; + } + // backwards compatibility: assume true when `enable` is omitted - if (this._handle && this._handle.setNoDelay) + if (this._handle.setNoDelay) this._handle.setNoDelay(enable === undefined ? true : !!enable); }; Socket.prototype.setKeepAlive = function(setting, msecs) { - if (this._handle && this._handle.setKeepAlive) + if (!this._handle) { + this.once('connect', this.setKeepAlive.bind(this, setting, msecs)); + return; + } + + if (this._handle.setKeepAlive) this._handle.setKeepAlive(setting, ~~(msecs / 1000)); }; @@ -881,64 +892,81 @@ Socket.prototype.connect = function(options, cb) { connect(self, options.path); } else { - const dns = require('dns'); - var host = options.host || 'localhost'; - var port = 0; - var localAddress = options.localAddress; - var localPort = options.localPort; - var dnsopts = { - family: options.family, - hints: 0 - }; - - if (localAddress && !exports.isIP(localAddress)) - throw new TypeError('localAddress must be a valid IP: ' + localAddress); - - if (localPort && typeof localPort !== 'number') - throw new TypeError('localPort should be a number: ' + localPort); - - port = options.port; - if (typeof port !== 'undefined') { - if (typeof port !== 'number' && typeof port !== 'string') - throw new TypeError('port should be a number or string: ' + port); - if (!isLegalPort(port)) - throw new RangeError('port should be >= 0 and < 65536: ' + port); - } - port |= 0; + lookupAndConnect(self, options); + } + return self; +}; - if (dnsopts.family !== 4 && dnsopts.family !== 6) - dnsopts.hints = dns.ADDRCONFIG | dns.V4MAPPED; - debug('connect: find host ' + host); - debug('connect: dns options ' + dnsopts); - self._host = host; - dns.lookup(host, dnsopts, function(err, ip, addressType) { - self.emit('lookup', err, ip, addressType); +function lookupAndConnect(self, options) { + const dns = require('dns'); + var host = options.host || 'localhost'; + var port = options.port; + var localAddress = options.localAddress; + var localPort = options.localPort; - // It's possible we were destroyed while looking this up. - // XXX it would be great if we could cancel the promise returned by - // the look up. - if (!self._connecting) return; + if (localAddress && !exports.isIP(localAddress)) + throw new TypeError('localAddress must be a valid IP: ' + localAddress); - if (err) { - // net.createConnection() creates a net.Socket object and - // immediately calls net.Socket.connect() on it (that's us). - // There are no event listeners registered yet so defer the - // error event to the next tick. - process.nextTick(connectErrorNT, self, err, options); - } else { - self._unrefTimer(); - connect(self, - ip, - port, - addressType, - localAddress, - localPort); - } - }); + if (localPort && typeof localPort !== 'number') + throw new TypeError('localPort should be a number: ' + localPort); + + if (typeof port !== 'undefined') { + if (typeof port !== 'number' && typeof port !== 'string') + throw new TypeError('port should be a number or string: ' + port); + if (!isLegalPort(port)) + throw new RangeError('port should be >= 0 and < 65536: ' + port); } - return self; -}; + port |= 0; + + // If host is an IP, skip performing a lookup + // TODO(evanlucas) should we hot path this for localhost? + var addressType = exports.isIP(host); + if (addressType) { + connect(self, host, port, addressType, localAddress, localPort); + return; + } + + if (options.lookup && typeof options.lookup !== 'function') + throw new TypeError('options.lookup should be a function.'); + + var dnsopts = { + family: options.family, + hints: 0 + }; + + if (dnsopts.family !== 4 && dnsopts.family !== 6) + dnsopts.hints = dns.ADDRCONFIG | dns.V4MAPPED; + + debug('connect: find host ' + host); + debug('connect: dns options ' + dnsopts); + self._host = host; + var lookup = options.lookup || dns.lookup; + lookup(host, dnsopts, function(err, ip, addressType) { + self.emit('lookup', err, ip, addressType); + + // It's possible we were destroyed while looking this up. + // XXX it would be great if we could cancel the promise returned by + // the look up. + if (!self._connecting) return; + + if (err) { + // net.createConnection() creates a net.Socket object and + // immediately calls net.Socket.connect() on it (that's us). + // There are no event listeners registered yet so defer the + // error event to the next tick. + process.nextTick(connectErrorNT, self, err, options); + } else { + self._unrefTimer(); + connect(self, + ip, + port, + addressType, + localAddress, + localPort); + } + }); +} function connectErrorNT(self, err, options) { @@ -951,14 +979,22 @@ function connectErrorNT(self, err, options) { Socket.prototype.ref = function() { - if (this._handle) - this._handle.ref(); + if (!this._handle) { + this.once('connect', this.ref); + return; + } + + this._handle.ref(); }; Socket.prototype.unref = function() { - if (this._handle) - this._handle.unref(); + if (!this._handle) { + this.once('connect', this.unref); + return; + } + + this._handle.unref(); }; diff --git a/src/node_version.h b/src/node_version.h index ad48f98a36923f..d24973bde12427 100644 --- a/src/node_version.h +++ b/src/node_version.h @@ -1,11 +1,11 @@ #ifndef SRC_NODE_VERSION_H_ #define SRC_NODE_VERSION_H_ -#define NODE_MAJOR_VERSION 1 -#define NODE_MINOR_VERSION 8 -#define NODE_PATCH_VERSION 1 +#define NODE_MAJOR_VERSION 2 +#define NODE_MINOR_VERSION 0 +#define NODE_PATCH_VERSION 0 -#define NODE_VERSION_IS_RELEASE 1 +#define NODE_VERSION_IS_RELEASE 0 #ifndef NODE_STRINGIFY #define NODE_STRINGIFY(n) NODE_STRINGIFY_HELPER(n) diff --git a/test/parallel/test-http-request-dont-override-options.js b/test/parallel/test-http-request-dont-override-options.js new file mode 100644 index 00000000000000..66d82caeac930d --- /dev/null +++ b/test/parallel/test-http-request-dont-override-options.js @@ -0,0 +1,45 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +var requests = 0; + +http.createServer(function(req, res) { + res.writeHead(200); + res.end('ok'); + + requests++; +}).listen(common.PORT).unref(); + +var agent = new http.Agent(); +agent.defaultPort = common.PORT; + +// options marked as explicitly undefined for readability +// in this test, they should STAY undefined as options should not +// be mutable / modified +var options = { + host: undefined, + hostname: common.localhostIPv4, + port: undefined, + defaultPort: undefined, + path: undefined, + method: undefined, + agent: agent +}; + +http.request(options, function(res) { + res.resume(); +}).end(); + +process.on('exit', function() { + assert.equal(requests, 1); + + assert.strictEqual(options.host, undefined); + assert.strictEqual(options.hostname, common.localhostIPv4); + assert.strictEqual(options.port, undefined); + assert.strictEqual(options.defaultPort, undefined); + assert.strictEqual(options.path, undefined); + assert.strictEqual(options.method, undefined); +}); diff --git a/test/parallel/test-module-globalpaths-nodepath.js b/test/parallel/test-module-globalpaths-nodepath.js index d0261e81f7a802..6cfa17933ce869 100644 --- a/test/parallel/test-module-globalpaths-nodepath.js +++ b/test/parallel/test-module-globalpaths-nodepath.js @@ -6,20 +6,22 @@ var module = require('module'); var isWindows = process.platform === 'win32'; var partA, partB; +var partC = ''; if (isWindows) { partA = 'C:\\Users\\Rocko Artischocko\\AppData\\Roaming\\npm'; partB = 'C:\\Program Files (x86)\\nodejs\\'; - process.env['NODE_PATH'] = partA + ';' + partB; + process.env['NODE_PATH'] = partA + ';' + partB + ';' + partC; } else { partA = '/usr/test/lib/node_modules'; partB = '/usr/test/lib/node'; - process.env['NODE_PATH'] = partA + ':' + partB; + process.env['NODE_PATH'] = partA + ':' + partB + ':' + partC; } module._initPaths(); assert.ok(module.globalPaths.indexOf(partA) !== -1); assert.ok(module.globalPaths.indexOf(partB) !== -1); +assert.ok(module.globalPaths.indexOf(partC) === -1); -assert.ok(Array.isArray(module.globalPaths)); \ No newline at end of file +assert.ok(Array.isArray(module.globalPaths)); diff --git a/test/parallel/test-net-dns-custom-lookup.js b/test/parallel/test-net-dns-custom-lookup.js new file mode 100644 index 00000000000000..3979bbf0b6d2c5 --- /dev/null +++ b/test/parallel/test-net-dns-custom-lookup.js @@ -0,0 +1,40 @@ +var common = require('../common'); +var assert = require('assert'); +var net = require('net'); +var dns = require('dns'); +var ok = false; + +function check(addressType, cb) { + var server = net.createServer(function(client) { + client.end(); + server.close(); + cb && cb(); + }); + + var address = addressType === 4 ? '127.0.0.1' : '::1'; + server.listen(common.PORT, address, function() { + net.connect({ + port: common.PORT, + host: 'localhost', + lookup: lookup + }).on('lookup', function(err, ip, type) { + assert.equal(err, null); + assert.equal(ip, address); + assert.equal(type, addressType); + ok = true; + }); + }); + + function lookup(host, dnsopts, cb) { + dnsopts.family = addressType; + dns.lookup(host, dnsopts, cb); + } +} + +check(4, function() { + common.hasIPv6 && check(6); +}); + +process.on('exit', function() { + assert.ok(ok); +}); diff --git a/test/parallel/test-net-dns-lookup-skip.js b/test/parallel/test-net-dns-lookup-skip.js new file mode 100644 index 00000000000000..7a129b979510b7 --- /dev/null +++ b/test/parallel/test-net-dns-lookup-skip.js @@ -0,0 +1,18 @@ +var common = require('../common'); +var assert = require('assert'); +var net = require('net'); + +function check(addressType) { + var server = net.createServer(function(client) { + client.end(); + server.close(); + }); + + var address = addressType === 4 ? '127.0.0.1' : '::1'; + server.listen(common.PORT, address, function() { + net.connect(common.PORT, address).on('lookup', assert.fail); + }); +} + +check(4); +common.hasIPv6 && check(6); diff --git a/test/parallel/test-net-dns-lookup.js b/test/parallel/test-net-dns-lookup.js index e7c058fe144a56..92ba794d74520c 100644 --- a/test/parallel/test-net-dns-lookup.js +++ b/test/parallel/test-net-dns-lookup.js @@ -9,7 +9,7 @@ var server = net.createServer(function(client) { }); server.listen(common.PORT, '127.0.0.1', function() { - net.connect(common.PORT, '127.0.0.1').on('lookup', function(err, ip, type) { + net.connect(common.PORT, 'localhost').on('lookup', function(err, ip, type) { assert.equal(err, null); assert.equal(ip, '127.0.0.1'); assert.equal(type, '4'); diff --git a/test/parallel/test-net-persistent-keepalive.js b/test/parallel/test-net-persistent-keepalive.js new file mode 100644 index 00000000000000..86936fd2a517e8 --- /dev/null +++ b/test/parallel/test-net-persistent-keepalive.js @@ -0,0 +1,32 @@ +var common = require('../common'); +var assert = require('assert'); +var net = require('net'); + +var serverConnection; +var echoServer = net.createServer(function(connection) { + serverConnection = connection; + connection.setTimeout(0); + assert.notEqual(connection.setKeepAlive, undefined); + connection.on('end', function() { + connection.end(); + }); +}); +echoServer.listen(common.PORT); + +echoServer.on('listening', function() { + var clientConnection = new net.Socket(); + // send a keepalive packet after 1000 ms + // and make sure it persists + clientConnection.setKeepAlive(true, 400); + clientConnection.connect(common.PORT); + clientConnection.setTimeout(0); + + setTimeout(function() { + // make sure both connections are still open + assert.equal(serverConnection.readyState, 'open'); + assert.equal(clientConnection.readyState, 'open'); + serverConnection.end(); + clientConnection.end(); + echoServer.close(); + }, 600); +}); diff --git a/test/parallel/test-net-persistent-nodelay.js b/test/parallel/test-net-persistent-nodelay.js new file mode 100644 index 00000000000000..791c3b7df7478b --- /dev/null +++ b/test/parallel/test-net-persistent-nodelay.js @@ -0,0 +1,33 @@ +var common = require('../common'); +var assert = require('assert'); +var net = require('net'); +var TCPWrap = process.binding('tcp_wrap').TCP; + +var echoServer = net.createServer(function(connection) { + connection.end(); +}); +echoServer.listen(common.PORT); + +var setTrue = 0; + +var Socket = net.Socket; +var setNoDelay = TCPWrap.prototype.setNoDelay; + +TCPWrap.prototype.setNoDelay = function(enable) { + setNoDelay.call(this, enable); + setTrue++; +}; + +echoServer.on('listening', function() { + var sock1 = new Socket(); + // setNoDelay before the handle is created + // there is probably a better way to test this + + sock1.setNoDelay(); + sock1.connect(common.PORT); + sock1.on('end', process.exit); +}); + +process.on('exit', function() { + assert.ok(setTrue); +}); diff --git a/test/parallel/test-net-persistent-ref-unref.js b/test/parallel/test-net-persistent-ref-unref.js new file mode 100644 index 00000000000000..0474d0053458ff --- /dev/null +++ b/test/parallel/test-net-persistent-ref-unref.js @@ -0,0 +1,39 @@ +var common = require('../common'); +var assert = require('assert'); +var net = require('net'); +var TCPWrap = process.binding('tcp_wrap').TCP; + +var echoServer = net.createServer(function(conn) { + conn.end(); +}); + +var ref = TCPWrap.prototype.ref; +var unref = TCPWrap.prototype.unref; + +var refed = false; +var unrefed = false; + +TCPWrap.prototype.ref = function() { + ref.call(this); + refed = true; +}; + +TCPWrap.prototype.unref = function() { + unref.call(this); + unrefed = true; +}; + +echoServer.listen(common.PORT); + +echoServer.on('listening', function() { + var sock = new net.Socket(); + sock.unref(); + sock.ref(); + sock.connect(common.PORT); + sock.on('end', process.exit); +}); + +process.on('exit', function() { + assert.ok(refed); + assert.ok(unrefed); +}); diff --git a/test/parallel/test-tls-connect-no-host.js b/test/parallel/test-tls-connect-no-host.js new file mode 100644 index 00000000000000..41aac1acabd781 --- /dev/null +++ b/test/parallel/test-tls-connect-no-host.js @@ -0,0 +1,34 @@ +var common = require('../common'); + +if (!common.hasCrypto) { + console.log('1..0 # Skipped: missing crypto'); + process.exit(); +} +var tls = require('tls'); + +var assert = require('assert'); +var fs = require('fs'); +var path = require('path'); + +var cert = fs.readFileSync(path.join(common.fixturesDir, 'test_cert.pem')); +var key = fs.readFileSync(path.join(common.fixturesDir, 'test_key.pem')); + +// https://github.com/iojs/io.js/issues/1489 +// tls.connect(options) with no options.host should accept a cert with +// CN:'localhost' +tls.createServer({ + key: key, + cert: cert +}).listen(common.PORT); + +var socket = tls.connect({ + port: common.PORT, + ca: cert, + // No host set here. 'localhost' is the default, + // but tls.checkServerIdentity() breaks before the fix with: + // Error: Hostname/IP doesn't match certificate's altnames: + // "Host: undefined. is not cert's CN: localhost" +}, function() { + assert(socket.authorized); + process.exit(); +});