'use strict'; // Flags: --expose-gc --expose-internals --no-warnings const common = require('../common'); const { internalBinding } = require('internal/test/binding'); const assert = require('assert'); const fs = require('fs'); const fsPromises = fs.promises; const net = require('net'); const providers = Object.assign({}, internalBinding('async_wrap').Providers); const fixtures = require('../common/fixtures'); const tmpdir = require('../common/tmpdir'); const { getSystemErrorName } = require('util'); // Make sure that all Providers are tested. { const hooks = require('async_hooks').createHook({ init(id, type) { if (type === 'NONE') throw new Error('received a provider type of NONE'); delete providers[type]; }, }).enable(); process.on('beforeExit', common.mustCall(() => { // This garbage collection call verifies that the wraps being garbage // collected doesn't resurrect the process again due to weirdly timed // uv_close calls and other similar instruments in destructors. global.gc(); process.removeAllListeners('uncaughtException'); hooks.disable(); delete providers.NONE; // Should never be used. // See test/pseudo-tty/test-async-wrap-getasyncid-tty.js // Requires an 'actual' tty fd to be available. delete providers.TTYWRAP; // TODO(jasnell): Test for these delete providers.HTTP2SESSION; delete providers.HTTP2STREAM; delete providers.HTTP2PING; delete providers.HTTP2SETTINGS; // TODO(addaleax): Test for these delete providers.STREAMPIPE; delete providers.MESSAGEPORT; delete providers.WORKER; if (!common.isMainThread) delete providers.INSPECTORJSBINDING; delete providers.KEYPAIRGENREQUEST; const objKeys = Object.keys(providers); if (objKeys.length > 0) process._rawDebug(objKeys); assert.strictEqual(objKeys.length, 0); })); } function testUninitialized(req, ctor_name) { assert.strictEqual(typeof req.getAsyncId, 'function'); assert.strictEqual(req.getAsyncId(), -1); assert.strictEqual(req.constructor.name, ctor_name); } function testInitialized(req, ctor_name) { assert.strictEqual(typeof req.getAsyncId, 'function'); assert(Number.isSafeInteger(req.getAsyncId())); assert(req.getAsyncId() > 0); assert.strictEqual(req.constructor.name, ctor_name); } { const cares = internalBinding('cares_wrap'); const dns = require('dns'); testUninitialized(new cares.GetAddrInfoReqWrap(), 'GetAddrInfoReqWrap'); testUninitialized(new cares.GetNameInfoReqWrap(), 'GetNameInfoReqWrap'); testUninitialized(new cares.QueryReqWrap(), 'QueryReqWrap'); testInitialized(dns.lookup('www.google.com', () => {}), 'GetAddrInfoReqWrap'); testInitialized(dns.lookupService('::1', 22, () => {}), 'GetNameInfoReqWrap'); const resolver = new dns.Resolver(); resolver.setServers(['127.0.0.1']); testInitialized(resolver._handle, 'ChannelWrap'); testInitialized(resolver.resolve6('::1', () => {}), 'QueryReqWrap'); resolver.cancel(); } { const FSEvent = internalBinding('fs_event_wrap').FSEvent; testInitialized(new FSEvent(), 'FSEvent'); } { const JSStream = internalBinding('js_stream').JSStream; testInitialized(new JSStream(), 'JSStream'); } { // We don't want to expose getAsyncId for promises but we need to construct // one so that the corresponding provider type is removed from the // providers list. new Promise((res) => res(5)); } if (common.hasCrypto) { // eslint-disable-line node-core/crypto-check const crypto = require('crypto'); // The handle for PBKDF2 and RandomBytes isn't returned by the function call, // so need to check it from the callback. const mc = common.mustCall(function pb() { testInitialized(this, 'AsyncWrap'); }); crypto.pbkdf2('password', 'salt', 1, 20, 'sha256', mc); crypto.randomBytes(1, common.mustCall(function rb() { testInitialized(this, 'AsyncWrap'); })); if (typeof internalBinding('crypto').scrypt === 'function') { crypto.scrypt('password', 'salt', 8, common.mustCall(function() { testInitialized(this, 'AsyncWrap'); })); } } { const binding = internalBinding('fs'); const path = require('path'); const FSReqCallback = binding.FSReqCallback; const req = new FSReqCallback(); req.oncomplete = () => { }; testInitialized(req, 'FSReqCallback'); binding.access(path.toNamespacedPath('../'), fs.F_OK, req); const StatWatcher = binding.StatWatcher; testInitialized(new StatWatcher(), 'StatWatcher'); } { const { HTTPParser } = require('_http_common'); testInitialized(new HTTPParser(HTTPParser.REQUEST), 'HTTPParser'); } { const Gzip = require('zlib').Gzip; testInitialized(new Gzip()._handle, 'Zlib'); } { const binding = internalBinding('pipe_wrap'); const handle = new binding.Pipe(binding.constants.IPC); testInitialized(handle, 'Pipe'); } { tmpdir.refresh(); const server = net.createServer(common.mustCall((socket) => { server.close(); })).listen(common.PIPE, common.mustCall(() => { const binding = internalBinding('pipe_wrap'); const handle = new binding.Pipe(binding.constants.SOCKET); testInitialized(handle, 'Pipe'); const req = new binding.PipeConnectWrap(); testUninitialized(req, 'PipeConnectWrap'); req.address = common.PIPE; req.oncomplete = common.mustCall(() => handle.close()); handle.connect(req, req.address, req.oncomplete); testInitialized(req, 'PipeConnectWrap'); })); } { const Process = internalBinding('process_wrap').Process; testInitialized(new Process(), 'Process'); } { const { Signal } = internalBinding('signal_wrap'); testInitialized(new Signal(), 'Signal'); } { async function openTest() { const fd = await fsPromises.open(__filename, 'r'); testInitialized(fd, 'FileHandle'); await fd.close(); } openTest().then(common.mustCall()); } { const binding = internalBinding('stream_wrap'); testUninitialized(new binding.WriteWrap(), 'WriteWrap'); } { const stream_wrap = internalBinding('stream_wrap'); const tcp_wrap = internalBinding('tcp_wrap'); const server = net.createServer(common.mustCall((socket) => { server.close(); socket.on('data', () => { socket.end(); socket.destroy(); }); socket.resume(); })).listen(0, common.localhostIPv4, common.mustCall(() => { const handle = new tcp_wrap.TCP(tcp_wrap.constants.SOCKET); const req = new tcp_wrap.TCPConnectWrap(); const sreq = new stream_wrap.ShutdownWrap(); testInitialized(handle, 'TCP'); testUninitialized(req, 'TCPConnectWrap'); testUninitialized(sreq, 'ShutdownWrap'); sreq.oncomplete = common.mustCall(() => { handle.close(); }); req.oncomplete = common.mustCall(writeData); function writeData() { const wreq = new stream_wrap.WriteWrap(); wreq.handle = handle; wreq.oncomplete = () => { handle.shutdown(sreq); testInitialized(sreq, 'ShutdownWrap'); }; const err = handle.writeLatin1String(wreq, 'hi'.repeat(100000)); if (err) throw new Error(`write failed: ${getSystemErrorName(err)}`); if (!stream_wrap.streamBaseState[stream_wrap.kLastWriteWasAsync]) { testUninitialized(wreq, 'WriteWrap'); // Synchronous finish. Write more data until we hit an // asynchronous write. return writeData(); } testInitialized(wreq, 'WriteWrap'); } req.address = common.localhostIPv4; req.port = server.address().port; const err = handle.connect(req, req.address, req.port); assert.strictEqual(err, 0); testInitialized(req, 'TCPConnectWrap'); })); } if (common.hasCrypto) { // eslint-disable-line node-core/crypto-check const { TCP, constants: TCPConstants } = internalBinding('tcp_wrap'); const tcp = new TCP(TCPConstants.SOCKET); const ca = fixtures.readSync('test_ca.pem', 'ascii'); const cert = fixtures.readSync('test_cert.pem', 'ascii'); const key = fixtures.readSync('test_key.pem', 'ascii'); const credentials = require('tls').createSecureContext({ ca, cert, key }); // TLSWrap is exposed, but needs to be instantiated via tls_wrap.wrap(). const tls_wrap = internalBinding('tls_wrap'); testInitialized( tls_wrap.wrap(tcp._externalStream, credentials.context, true), 'TLSWrap'); } { const binding = internalBinding('udp_wrap'); const handle = new binding.UDP(); const req = new binding.SendWrap(); testInitialized(handle, 'UDP'); testUninitialized(req, 'SendWrap'); handle.bind('0.0.0.0', 0, undefined); const addr = {}; handle.getsockname(addr); req.address = '127.0.0.1'; req.port = addr.port; req.oncomplete = () => handle.close(); handle.send(req, [Buffer.alloc(1)], 1, req.port, req.address, true); testInitialized(req, 'SendWrap'); } if (process.config.variables.v8_enable_inspector !== 0 && common.isMainThread) { const binding = internalBinding('inspector'); const handle = new binding.Connection(() => {}); testInitialized(handle, 'Connection'); handle.disconnect(); }