fs: add runtime deprecate for file stream open()

WriteStream.open() and ReadStream.open() are undocumented internal
APIs that do not make sense to use in userland. File streams should
always be opened through their corresponding factory methods
(fs.createWriteStream() and fs.createReadStream()) or by passing a file
descriptor in options.

PR-URL: https://github.com/nodejs/node/pull/29061
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
Reviewed-By: João Reis <reis@janeasystems.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
This commit is contained in:
Robert Nagy 2019-08-09 09:01:43 +02:00 committed by Rich Trott
parent 039eb56249
commit 773769df60
4 changed files with 113 additions and 21 deletions

View File

@ -2532,6 +2532,22 @@ Type: Documentation-only (supports [`--pending-deprecation`][])
The `process._tickCallback` property was never documented as
an officially supported API.
<a id="DEP0XXX"></a>
### DEP0XXX: `WriteStream.open()` and `ReadStream.open()` are internal
<!-- YAML
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/29061
description: Runtime deprecation.
-->
Type: Runtime
[`WriteStream.open()`][] and [`ReadStream.open()`][] are undocumented internal
APIs that do not make sense to use in userland. File streams should always be
opened through their corresponding factory methods [`fs.createWriteStream()`][]
and [`fs.createReadStream()`][]) or by passing a file descriptor in options.
[`--pending-deprecation`]: cli.html#cli_pending_deprecation
[`--throw-deprecation`]: cli.html#cli_throw_deprecation
[`Buffer.allocUnsafeSlow(size)`]: buffer.html#buffer_class_method_buffer_allocunsafeslow_size
@ -2542,10 +2558,12 @@ an officially supported API.
[`Decipher`]: crypto.html#crypto_class_decipher
[`EventEmitter.listenerCount(emitter, eventName)`]: events.html#events_eventemitter_listenercount_emitter_eventname
[`REPLServer.clearBufferedCommand()`]: repl.html#repl_replserver_clearbufferedcommand
[`ReadStream.open()`]: fs.html#fs_class_fs_readstream
[`Server.connections`]: net.html#net_server_connections
[`Server.getConnections()`]: net.html#net_server_getconnections_callback
[`Server.listen({fd: <number>})`]: net.html#net_server_listen_handle_backlog_callback
[`SlowBuffer`]: buffer.html#buffer_class_slowbuffer
[`WriteStream.open()`]: fs.html#fs_class_fs_writestream
[`assert`]: assert.html
[`asyncResource.runInAsyncScope()`]: async_hooks.html#async_hooks_asyncresource_runinasyncscope_fn_thisarg_args
[`child_process`]: child_process.html
@ -2568,6 +2586,8 @@ an officially supported API.
[`ecdh.setPublicKey()`]: crypto.html#crypto_ecdh_setpublickey_publickey_encoding
[`emitter.listenerCount(eventName)`]: events.html#events_emitter_listenercount_eventname
[`fs.access()`]: fs.html#fs_fs_access_path_mode_callback
[`fs.createReadStream()`]: fs.html#fs_fs_createreadstream_path_options
[`fs.createWriteStream()`]: fs.html#fs_fs_createwritestream_path_options
[`fs.exists(path, callback)`]: fs.html#fs_fs_exists_path_callback
[`fs.lchmod(path, mode, callback)`]: fs.html#fs_fs_lchmod_path_mode_callback
[`fs.lchmodSync(path, mode)`]: fs.html#fs_fs_lchmodsync_path_mode

View File

@ -5,6 +5,7 @@ const { Math, Object } = primordials;
const {
ERR_OUT_OF_RANGE
} = require('internal/errors').codes;
const internalUtil = require('internal/util');
const { validateNumber } = require('internal/validators');
const fs = require('fs');
const { Buffer } = require('buffer');
@ -100,7 +101,7 @@ function ReadStream(path, options) {
}
if (typeof this.fd !== 'number')
this.open();
_openReadFs(this);
this.on('end', function() {
if (this.autoClose) {
@ -111,23 +112,34 @@ function ReadStream(path, options) {
Object.setPrototypeOf(ReadStream.prototype, Readable.prototype);
Object.setPrototypeOf(ReadStream, Readable);
ReadStream.prototype.open = function() {
fs.open(this.path, this.flags, this.mode, (er, fd) => {
const openReadFs = internalUtil.deprecate(function() {
_openReadFs(this);
}, 'ReadStream.prototype.open() is deprecated', 'DEP0XXX');
ReadStream.prototype.open = openReadFs;
function _openReadFs(stream) {
// Backwards compat for overriden open.
if (stream.open !== openReadFs) {
stream.open();
return;
}
fs.open(stream.path, stream.flags, stream.mode, (er, fd) => {
if (er) {
if (this.autoClose) {
this.destroy();
if (stream.autoClose) {
stream.destroy();
}
this.emit('error', er);
stream.emit('error', er);
return;
}
this.fd = fd;
this.emit('open', fd);
this.emit('ready');
stream.fd = fd;
stream.emit('open', fd);
stream.emit('ready');
// Start the flow of data.
this.read();
stream.read();
});
};
}
ReadStream.prototype._read = function(n) {
if (typeof this.fd !== 'number') {
@ -266,7 +278,7 @@ function WriteStream(path, options) {
this.setDefaultEncoding(options.encoding);
if (typeof this.fd !== 'number')
this.open();
_openWriteFs(this);
}
Object.setPrototypeOf(WriteStream.prototype, Writable.prototype);
Object.setPrototypeOf(WriteStream, Writable);
@ -279,21 +291,32 @@ WriteStream.prototype._final = function(callback) {
callback();
};
WriteStream.prototype.open = function() {
fs.open(this.path, this.flags, this.mode, (er, fd) => {
const openWriteFs = internalUtil.deprecate(function() {
_openWriteFs(this);
}, 'WriteStream.prototype.open() is deprecated', 'DEP0XXX');
WriteStream.prototype.open = openWriteFs;
function _openWriteFs(stream) {
// Backwards compat for overriden open.
if (stream.open !== openWriteFs) {
stream.open();
return;
}
fs.open(stream.path, stream.flags, stream.mode, (er, fd) => {
if (er) {
if (this.autoClose) {
this.destroy();
if (stream.autoClose) {
stream.destroy();
}
this.emit('error', er);
stream.emit('error', er);
return;
}
this.fd = fd;
this.emit('open', fd);
this.emit('ready');
stream.fd = fd;
stream.emit('open', fd);
stream.emit('ready');
});
};
}
WriteStream.prototype._write = function(data, encoding, cb) {

View File

@ -0,0 +1,15 @@
'use strict';
const common = require('../common');
const fs = require('fs');
common.expectWarning(
'DeprecationWarning',
'ReadStream.prototype.open() is deprecated', 'DEP0XXX');
const s = fs.createReadStream('asd')
// We don't care about errors in this test.
.on('error', () => {});
s.open();
// Allow overriding open().
fs.ReadStream.prototype.open = common.mustCall();
fs.createReadStream('asd');

View File

@ -0,0 +1,34 @@
'use strict';
const common = require('../common');
const fs = require('fs');
const tmpdir = require('../common/tmpdir');
// Run in a child process because 'out' is opened twice, blocking the tmpdir
// and preventing cleanup.
if (process.argv[2] !== 'child') {
// Parent
const assert = require('assert');
const { fork } = require('child_process');
tmpdir.refresh();
// Run test
const child = fork(__filename, ['child'], { stdio: 'inherit' });
child.on('exit', common.mustCall(function(code) {
assert.strictEqual(code, 0);
}));
return;
}
// Child
common.expectWarning(
'DeprecationWarning',
'WriteStream.prototype.open() is deprecated', 'DEP0XXX');
const s = fs.createWriteStream(`${tmpdir.path}/out`);
s.open();
// Allow overriding open().
fs.WriteStream.prototype.open = common.mustCall();
fs.createWriteStream('asd');