diff --git a/lib/fs.js b/lib/fs.js index 510369e44fd4af..69a66e1b3b2ca2 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -1984,6 +1984,7 @@ ReadStream.prototype.open = function() { self.fd = fd; self.emit('open', fd); + self.emit('ready', fd); // start the flow of data. self.read(); }); @@ -2141,6 +2142,7 @@ WriteStream.prototype.open = function() { this.fd = fd; this.emit('open', fd); + this.emit('ready', fd); }); }; diff --git a/lib/net.js b/lib/net.js index 7095a5190a17ea..9247bfe3067aa1 100644 --- a/lib/net.js +++ b/lib/net.js @@ -1181,6 +1181,7 @@ function afterConnect(status, handle, req, readable, writable) { self._unrefTimer(); self.emit('connect'); + self.emit('ready'); // start the first read, or get an immediate EOF. // this doesn't actually consume any bytes, because len=0. diff --git a/test/parallel/test-fs-ready-event-stream.js b/test/parallel/test-fs-ready-event-stream.js new file mode 100644 index 00000000000000..bced69899d3c40 --- /dev/null +++ b/test/parallel/test-fs-ready-event-stream.js @@ -0,0 +1,219 @@ +'use strict'; +const common = require('../common'); + +const assert = require('assert'); +const fs = require('fs'); +const fixtures = require('../common/fixtures'); + +const fn = fixtures.path('elipses.txt'); +const rangeFile = fixtures.path('x.txt'); + +{ + let paused = false; + let bytesRead = 0; + + const file = fs.ReadStream(fn); + const fileSize = fs.statSync(fn).size; + + assert.strictEqual(file.bytesRead, 0); + + file.on('ready', common.mustCall(function(fd) { + file.length = 0; + assert.strictEqual('number', typeof fd); + assert.strictEqual(file.bytesRead, 0); + assert.ok(file.readable); + + // GH-535 + file.pause(); + file.resume(); + file.pause(); + file.resume(); + })); + + file.on('data', function(data) { + assert.ok(data instanceof Buffer); + assert.ok(!paused); + file.length += data.length; + + bytesRead += data.length; + assert.strictEqual(file.bytesRead, bytesRead); + + paused = true; + file.pause(); + + setTimeout(function() { + paused = false; + file.resume(); + }, 10); + }); + + + file.on('end', common.mustCall(function(chunk) { + assert.strictEqual(bytesRead, fileSize); + assert.strictEqual(file.bytesRead, fileSize); + })); + + + file.on('close', common.mustCall(function() { + assert.strictEqual(bytesRead, fileSize); + assert.strictEqual(file.bytesRead, fileSize); + })); + + process.on('exit', function() { + assert.strictEqual(file.length, 30000); + }); +} + +{ + const file = fs.createReadStream(fn, { encoding: 'utf8' }); + file.length = 0; + file.on('data', function(data) { + assert.strictEqual('string', typeof data); + file.length += data.length; + + for (let i = 0; i < data.length; i++) { + // http://www.fileformat.info/info/unicode/char/2026/index.htm + assert.strictEqual('\u2026', data[i]); + } + }); + + file.on('close', common.mustCall()); + + process.on('exit', function() { + assert.strictEqual(file.length, 10000); + }); +} + +{ + const file = + fs.createReadStream(rangeFile, { bufferSize: 1, start: 1, end: 2 }); + let contentRead = ''; + file.on('data', function(data) { + contentRead += data.toString('utf-8'); + }); + file.on('end', common.mustCall(function(data) { + assert.strictEqual(contentRead, 'yz'); + })); +} + +{ + const file = fs.createReadStream(rangeFile, { bufferSize: 1, start: 1 }); + file.data = ''; + file.on('data', function(data) { + file.data += data.toString('utf-8'); + }); + file.on('end', common.mustCall(function() { + assert.strictEqual(file.data, 'yz\n'); + })); +} + +{ + // Ref: https://github.com/nodejs/node-v0.x-archive/issues/2320 + const file = fs.createReadStream(rangeFile, { bufferSize: 1.23, start: 1 }); + file.data = ''; + file.on('data', function(data) { + file.data += data.toString('utf-8'); + }); + file.on('end', common.mustCall(function() { + assert.strictEqual(file.data, 'yz\n'); + })); +} + +common.expectsError( + () => { + fs.createReadStream(rangeFile, { start: 10, end: 2 }); + }, + { + code: 'ERR_OUT_OF_RANGE', + message: 'The value of "start" is out of range. It must be <= "end". ' + + 'Received {start: 10, end: 2}', + type: RangeError + }); + +{ + const stream = fs.createReadStream(rangeFile, { start: 0, end: 0 }); + stream.data = ''; + + stream.on('data', function(chunk) { + stream.data += chunk; + }); + + stream.on('end', common.mustCall(function() { + assert.strictEqual('x', stream.data); + })); +} + +{ + // Verify that end works when start is not specified. + const stream = new fs.createReadStream(rangeFile, { end: 1 }); + stream.data = ''; + + stream.on('data', function(chunk) { + stream.data += chunk; + }); + + stream.on('end', common.mustCall(function() { + assert.strictEqual('xy', stream.data); + })); +} + +{ + // pause and then resume immediately. + const pauseRes = fs.createReadStream(rangeFile); + pauseRes.pause(); + pauseRes.resume(); +} + +{ + let file = fs.createReadStream(rangeFile, { autoClose: false }); + let data = ''; + file.on('data', function(chunk) { data += chunk; }); + file.on('end', common.mustCall(function() { + assert.strictEqual(data, 'xyz\n'); + process.nextTick(function() { + assert(!file.closed); + assert(!file.destroyed); + fileNext(); + }); + })); + + function fileNext() { + // This will tell us if the fd is usable again or not. + file = fs.createReadStream(null, { fd: file.fd, start: 0 }); + file.data = ''; + file.on('data', function(data) { + file.data += data; + }); + file.on('end', common.mustCall(function(err) { + assert.strictEqual(file.data, 'xyz\n'); + })); + process.on('exit', function() { + assert(file.closed); + assert(file.destroyed); + }); + } +} + +{ + // Just to make sure autoClose won't close the stream because of error. + const file = fs.createReadStream(null, { fd: 13337, autoClose: false }); + file.on('data', common.mustNotCall()); + file.on('error', common.mustCall()); + process.on('exit', function() { + assert(!file.closed); + assert(!file.destroyed); + assert(file.fd); + }); +} + +{ + // Make sure stream is destroyed when file does not exist. + const file = fs.createReadStream('/path/to/file/that/does/not/exist'); + file.on('data', common.mustNotCall()); + file.on('error', common.mustCall()); + + process.on('exit', function() { + assert(!file.closed); + assert(file.destroyed); + }); +} diff --git a/test/parallel/test-net-socket-ready-without-cb.js b/test/parallel/test-net-socket-ready-without-cb.js new file mode 100644 index 00000000000000..1ad5db4760dc1e --- /dev/null +++ b/test/parallel/test-net-socket-ready-without-cb.js @@ -0,0 +1,20 @@ +'use strict'; +const common = require('../common'); + +// This test ensures that socket.connect can be called without callback +// which is optional. + +const net = require('net'); + +const server = net.createServer(common.mustCall(function(conn) { + conn.end(); + server.close(); +})).listen(0, common.mustCall(function() { + const client = new net.Socket(); + + client.on('ready', common.mustCall(function() { + client.end(); + })); + + client.connect(server.address()); +}));