diff --git a/_deno_unstable.ts b/_deno_unstable.ts index 5640430fa5bc..73e17a328fbb 100644 --- a/_deno_unstable.ts +++ b/_deno_unstable.ts @@ -2,6 +2,8 @@ // @ts-nocheck Bypass static errors for missing --unstable. export type HttpClient = Deno.HttpClient; +export type UnixConnectOptions = Deno.UnixConnectOptions; +export type UnixListenOptions = Deno.UnixListenOptions; export function addSignalListener( ...args: Parameters @@ -153,6 +155,18 @@ export function networkInterfaces( } } +export async function connect( + options: UnixConnectOptions, +): Promise { + return await Deno.connect(options); +} + +export function listen( + options: UnixListenOptions & { transport: "unix" }, +): ReturnType { + return Deno.listen(options); +} + export function ListenerRef( listener: Deno.Listener, ...args: Parameters diff --git a/node/README.md b/node/README.md index 6a7fef58d177..0f8fbf0f7479 100644 --- a/node/README.md +++ b/node/README.md @@ -112,7 +112,7 @@ workflow. $ deno task node:setup ``` -You can aditionally pass the `-y`/`-n` flag to use test cache or generating +You can additionally pass the `-y`/`-n` flag to use test cache or generating tests from scratch instead of being prompted at the moment of running it. ```zsh diff --git a/node/_events.mjs b/node/_events.mjs index 12552d3be148..9fdb08b6ea40 100644 --- a/node/_events.mjs +++ b/node/_events.mjs @@ -789,6 +789,7 @@ export function getEventListeners(emitterOrTarget, type) { return emitterOrTarget.listeners(type); } if (emitterOrTarget instanceof EventTarget) { + // TODO: kEvents is not defined const root = emitterOrTarget[kEvents].get(type); const listeners = []; let handler = root?.next; diff --git a/node/_tools/common.ts b/node/_tools/common.ts index ebe26e508c04..d6048d050353 100644 --- a/node/_tools/common.ts +++ b/node/_tools/common.ts @@ -41,7 +41,7 @@ export const ignoreList = Object.entries(config.ignore).reduce( export function getPathsFromTestSuites(suites: TestSuites): string[] { const testPaths: string[] = []; for (const [dir, paths] of Object.entries(suites)) { - if (dir === "parallel" || dir === "internet") { + if (["parallel", "internet", "pummel"].includes(dir)) { for (const path of paths) { testPaths.push(join(dir, path)); } diff --git a/node/_tools/config.json b/node/_tools/config.json index 30b604099c03..39603c9c9e6a 100644 --- a/node/_tools/config.json +++ b/node/_tools/config.json @@ -32,6 +32,16 @@ "test-fs-rmdir-recursive.js", "test-fs-write-file.js", "test-fs-write.js", + "test-net-better-error-messages-path.js", + "test-net-connect-buffer.js", + "test-net-connect-buffer2.js", + "test-net-end-close.js", + "test-net-listen-invalid-port.js", + "test-net-server-call-listen-multiple-times.js", + "test-net-server-listen-path.js", + "test-net-server-try-ports.js", + "test-net-socket-timeout.js", + "test-net-write-arguments.js", "test-os.js", "test-path-resolve.js", "test-path.js", @@ -56,6 +66,10 @@ "test-zlib-write-after-flush.js", "test-zlib-zero-byte.js", "test-zlib-zero-windowBits.js" + ], + "pummel": [ + "test-net-bytes-per-incoming-chunk-overhead.js", + "test-net-write-callbacks.js" ] }, "tests": { @@ -225,7 +239,85 @@ "test-http-url.parse-path.js", "test-http-url.parse-post.js", "test-http-url.parse-search.js", + "test-net-access-byteswritten.js", + "test-net-after-close.js", + "test-net-allow-half-open.js", + "test-net-better-error-messages-listen-path.js", + "test-net-better-error-messages-listen.js", + "test-net-better-error-messages-path.js", + "test-net-better-error-messages-port-hostname.js", + "test-net-bind-twice.js", + "test-net-buffersize.js", + "test-net-bytes-written-large.js", + "test-net-can-reset-timeout.js", + "test-net-connect-after-destroy.js", + "test-net-connect-buffer.js", + "test-net-connect-buffer2.js", + "test-net-connect-call-socket-connect.js", + "test-net-connect-destroy.js", + "test-net-connect-immediate-destroy.js", + "test-net-connect-immediate-finish.js", + "test-net-connect-no-arg.js", + "test-net-connect-options-ipv6.js", + "test-net-connect-options-port.js", + "test-net-dns-custom-lookup.js", + "test-net-dns-error.js", + "test-net-dns-lookup-skip.js", + "test-net-dns-lookup.js", + "test-net-during-close.js", + "test-net-eaddrinuse.js", + "test-net-end-close.js", + "test-net-end-destroyed.js", + "test-net-end-without-connect.js", + "test-net-isip.js", + "test-net-isipv4.js", + "test-net-isipv6.js", + "test-net-listen-after-destroying-stdin.js", + "test-net-listen-close-server-callback-is-not-function.js", + "test-net-listen-close-server.js", + "test-net-listen-error.js", + "test-net-listen-invalid-port.js", + "test-net-listening.js", + "test-net-local-address-port.js", + "test-net-localerror.js", + "test-net-options-lookup.js", + "test-net-pause-resume-connecting.js", + "test-net-persistent-ref-unref.js", + "test-net-pipe-connect-errors.js", + "test-net-remote-address-port.js", + "test-net-server-call-listen-multiple-times.js", + "test-net-server-capture-rejection.js", + "test-net-server-close.js", + "test-net-server-listen-options-signal.js", + "test-net-server-listen-options.js", + "test-net-server-listen-path.js", + "test-net-server-listen-remove-callback.js", + "test-net-server-max-connections.js", + "test-net-server-options.js", + "test-net-server-pause-on-connect.js", + "test-net-server-try-ports.js", "test-net-server-unref-persistent.js", + "test-net-server-unref.js", + "test-net-socket-close-after-end.js", + "test-net-socket-connect-without-cb.js", + "test-net-socket-connecting.js", + "test-net-socket-destroy-send.js", + "test-net-socket-destroy-twice.js", + "test-net-socket-end-before-connect.js", + "test-net-socket-end-callback.js", + "test-net-socket-no-halfopen-enforcer.js", + "test-net-socket-ready-without-cb.js", + "test-net-socket-timeout.js", + "test-net-socket-write-after-close.js", + "test-net-socket-write-error.js", + "test-net-sync-cork.js", + "test-net-timeout-no-handle.js", + "test-net-writable.js", + "test-net-write-after-end-nt.js", + "test-net-write-arguments.js", + "test-net-write-fully-async-buffer.js", + "test-net-write-fully-async-hex-string.js", + "test-net-write-slow.js", "test-next-tick-doesnt-hang.js", "test-next-tick-fixed-queue-regression.js", "test-next-tick-intentional-starvation.js", @@ -470,6 +562,11 @@ "test-zlib-write-after-flush.js", "test-zlib-zero-byte.js", "test-zlib-zero-windowBits.js" + ], + "pummel": [ + "test-net-bytes-per-incoming-chunk-overhead.js", + "test-net-pingpong-delay.js", + "test-net-write-callbacks.js" ] }, "windowsIgnore": { @@ -486,6 +583,10 @@ "test-fs-write-file-sync.js", "test-fs-write-file.js", "test-http-client-reject-cr-no-lf.js", + "test-net-better-error-messages-listen-path.js", + "test-net-better-error-messages-path.js", + "test-net-pipe-connect-errors.js", + "test-net-server-listen-path.js", "test-util-inspect-getters-accessing-this.js", "test-util-inspect-long-running.js", "test-util-inspect.js" diff --git a/node/_tools/setup.ts b/node/_tools/setup.ts index 5dae5787aa15..d4a8849e325f 100755 --- a/node/_tools/setup.ts +++ b/node/_tools/setup.ts @@ -27,7 +27,7 @@ import { downloadFile } from "../../_util/download_file.ts"; * * Usage: `deno run --allow-read --allow-net --allow-write setup.ts` * - * You can aditionally pass a flag to indicate if cache should be used for generating + * You can additionally pass a flag to indicate if cache should be used for generating * the tests, or to generate the tests from scratch (-y/-n) */ diff --git a/node/_tools/test/common/index.js b/node/_tools/test/common/index.js index 8eb429e3875f..71f944382f03 100644 --- a/node/_tools/test/common/index.js +++ b/node/_tools/test/common/index.js @@ -11,7 +11,9 @@ */ 'use strict'; const assert = require("assert"); +const path = require("path"); const util = require("util"); +const tmpdir = require("./tmpdir"); function platformTimeout(ms) { return ms; @@ -345,6 +347,13 @@ function skip(msg) { process.exit(0); } +const PIPE = (() => { + const localRelative = path.relative(process.cwd(), `${tmpdir.path}/`); + const pipePrefix = isWindows ? "\\\\.\\pipe\\" : localRelative; + const pipeName = `node-test.${process.pid}.sock`; + return path.join(pipePrefix, pipeName); +})(); + function getArrayBufferViews(buf) { const { buffer, byteOffset, byteLength } = buf; @@ -387,6 +396,7 @@ module.exports = { mustCallAtLeast, mustNotCall, mustSucceed, + PIPE, platformTimeout, printSkipMessage, skipIfDumbTerminal, diff --git a/node/_tools/test/parallel/test-net-access-byteswritten.js b/node/_tools/test/parallel/test-net-access-byteswritten.js new file mode 100644 index 000000000000..572626305d99 --- /dev/null +++ b/node/_tools/test/parallel/test-net-access-byteswritten.js @@ -0,0 +1,28 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const net = require('net'); +const tls = require('tls'); +const tty = require('tty'); + +// Check that the bytesWritten getter doesn't crash if object isn't +// constructed. +assert.strictEqual(net.Socket.prototype.bytesWritten, undefined); +assert.strictEqual(Object.getPrototypeOf(tls.TLSSocket).prototype.bytesWritten, + undefined); +assert.strictEqual(tls.TLSSocket.prototype.bytesWritten, undefined); +assert.strictEqual(Object.getPrototypeOf(tty.ReadStream).prototype.bytesWritten, + undefined); +assert.strictEqual(tty.ReadStream.prototype.bytesWritten, undefined); +assert.strictEqual(tty.WriteStream.prototype.bytesWritten, undefined); diff --git a/node/_tools/test/parallel/test-net-after-close.js b/node/_tools/test/parallel/test-net-after-close.js new file mode 100644 index 000000000000..fc8a9e405f86 --- /dev/null +++ b/node/_tools/test/parallel/test-net-after-close.js @@ -0,0 +1,58 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const server = net.createServer(common.mustCall((s) => { + console.error('SERVER: got connection'); + s.end(); +})); + +server.listen(0, common.mustCall(() => { + const c = net.createConnection(server.address().port); + c.on('close', common.mustCall(() => { + /* eslint-disable no-unused-expressions */ + console.error('connection closed'); + assert.strictEqual(c._handle, null); + // Calling functions / accessing properties of a closed socket should not + // throw. + c.setNoDelay(); + c.setKeepAlive(); + c.bufferSize; + c.pause(); + c.resume(); + c.address(); + c.remoteAddress; + c.remotePort; + server.close(); + /* eslint-enable no-unused-expressions */ + })); +})); diff --git a/node/_tools/test/parallel/test-net-allow-half-open.js b/node/_tools/test/parallel/test-net-allow-half-open.js new file mode 100644 index 000000000000..b05ac4cab160 --- /dev/null +++ b/node/_tools/test/parallel/test-net-allow-half-open.js @@ -0,0 +1,54 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +{ + const server = net.createServer(common.mustCall((socket) => { + socket.end(Buffer.alloc(1024)); + })).listen(0, common.mustCall(() => { + const socket = net.connect(server.address().port); + assert.strictEqual(socket.allowHalfOpen, false); + socket.resume(); + socket.on('end', common.mustCall(() => { + process.nextTick(() => { + // Ensure socket is not destroyed straight away + // without proper shutdown. + assert(!socket.destroyed); + server.close(); + }); + })); + socket.on('finish', common.mustCall(() => { + assert(!socket.destroyed); + })); + socket.on('close', common.mustCall()); + })); +} + +{ + const server = net.createServer(common.mustCall((socket) => { + socket.end(Buffer.alloc(1024)); + })).listen(0, common.mustCall(() => { + const socket = net.connect(server.address().port); + assert.strictEqual(socket.allowHalfOpen, false); + socket.resume(); + socket.on('end', common.mustCall(() => { + assert(!socket.destroyed); + })); + socket.end('asd'); + socket.on('finish', common.mustCall(() => { + assert(!socket.destroyed); + })); + socket.on('close', common.mustCall(() => { + server.close(); + })); + })); +} diff --git a/node/_tools/test/parallel/test-net-better-error-messages-listen-path.js b/node/_tools/test/parallel/test-net-better-error-messages-listen-path.js new file mode 100644 index 000000000000..147d102e577e --- /dev/null +++ b/node/_tools/test/parallel/test-net-better-error-messages-listen-path.js @@ -0,0 +1,17 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); +const fp = '/blah/fadfa'; +const server = net.createServer(common.mustNotCall()); +server.listen(fp, common.mustNotCall()); +server.on('error', common.mustCall(function(e) { + assert.strictEqual(e.address, fp); +})); diff --git a/node/_tools/test/parallel/test-net-better-error-messages-listen.js b/node/_tools/test/parallel/test-net-better-error-messages-listen.js new file mode 100644 index 000000000000..03047d971e5a --- /dev/null +++ b/node/_tools/test/parallel/test-net-better-error-messages-listen.js @@ -0,0 +1,19 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const server = net.createServer(common.mustNotCall()); +server.listen(1, '1.1.1.1', common.mustNotCall()); +server.on('error', common.mustCall(function(e) { + assert.strictEqual(e.address, '1.1.1.1'); + assert.strictEqual(e.port, 1); + assert.strictEqual(e.syscall, 'listen'); +})); diff --git a/node/_tools/test/parallel/test-net-better-error-messages-path.js b/node/_tools/test/parallel/test-net-better-error-messages-path.js new file mode 100644 index 000000000000..d1bada362dd7 --- /dev/null +++ b/node/_tools/test/parallel/test-net-better-error-messages-path.js @@ -0,0 +1,29 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +{ + const fp = '/tmp/fadagagsdfgsdf'; + const c = net.connect(fp); + + c.on('connect', common.mustNotCall()); + c.on('error', common.expectsError({ + code: 'ENOENT', + message: `connect ENOENT ${fp}` + })); +} + +{ + assert.throws( + () => net.createConnection({ path: {} }), + { code: 'ERR_INVALID_ARG_TYPE' } + ); +} diff --git a/node/_tools/test/parallel/test-net-better-error-messages-port-hostname.js b/node/_tools/test/parallel/test-net-better-error-messages-port-hostname.js new file mode 100644 index 000000000000..c492df0bc17d --- /dev/null +++ b/node/_tools/test/parallel/test-net-better-error-messages-port-hostname.js @@ -0,0 +1,44 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +// This tests that the error thrown from net.createConnection +// comes with host and port properties. +// See https://github.com/nodejs/node-v0.x-archive/issues/7005 + +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const { addresses } = require('../common/internet'); +const { + errorLookupMock, + mockedErrorCode +} = require('../common/dns'); + +// Using port 0 as hostname used is already invalid. +const c = net.createConnection({ + port: 0, + host: addresses.INVALID_HOST, + lookup: common.mustCall(errorLookupMock()) +}); + +c.on('connect', common.mustNotCall()); + +c.on('error', common.mustCall((error) => { + assert.ok(!('port' in error)); + assert.ok(!('host' in error)); + assert.throws(() => { throw error; }, { + errno: mockedErrorCode, + code: mockedErrorCode, + name: 'Error', + message: 'getaddrinfo ENOTFOUND something.invalid', + hostname: addresses.INVALID_HOST, + syscall: 'getaddrinfo' + }); +})); diff --git a/node/_tools/test/parallel/test-net-bind-twice.js b/node/_tools/test/parallel/test-net-bind-twice.js new file mode 100644 index 000000000000..dc5120504031 --- /dev/null +++ b/node/_tools/test/parallel/test-net-bind-twice.js @@ -0,0 +1,43 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const server1 = net.createServer(common.mustNotCall()); +server1.listen(0, '127.0.0.1', common.mustCall(function() { + const server2 = net.createServer(common.mustNotCall()); + server2.listen(this.address().port, '127.0.0.1', common.mustNotCall()); + + server2.on('error', common.mustCall(function(e) { + assert.strictEqual(e.code, 'EADDRINUSE'); + server1.close(); + })); +})); diff --git a/node/_tools/test/parallel/test-net-buffersize.js b/node/_tools/test/parallel/test-net-buffersize.js new file mode 100644 index 000000000000..b3203e599730 --- /dev/null +++ b/node/_tools/test/parallel/test-net-buffersize.js @@ -0,0 +1,59 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const iter = 10; + +const server = net.createServer(function(socket) { + socket.on('readable', function() { + socket.read(); + }); + + socket.on('end', function() { + server.close(); + }); +}); + +server.listen(0, common.mustCall(function() { + const client = net.connect(this.address().port); + + client.on('finish', common.mustCall(() => { + assert.strictEqual(client.bufferSize, 0); + })); + + for (let i = 1; i < iter; i++) { + client.write('a'); + assert.strictEqual(client.bufferSize, i); + } + + client.end(); +})); diff --git a/node/_tools/test/parallel/test-net-bytes-written-large.js b/node/_tools/test/parallel/test-net-bytes-written-large.js new file mode 100644 index 000000000000..a112c0c41a3d --- /dev/null +++ b/node/_tools/test/parallel/test-net-bytes-written-large.js @@ -0,0 +1,74 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +// Regression test for https://github.com/nodejs/node/issues/19562: +// Writing to a socket first tries to push through as much data as possible +// without blocking synchronously, and, if that is not enough, queues more +// data up for asynchronous writing. +// Check that `bytesWritten` accounts for both parts of a write. + +const N = 10000000; +{ + // Variant 1: Write a Buffer. + const server = net.createServer(common.mustCall((socket) => { + socket.end(Buffer.alloc(N), common.mustCall(() => { + assert.strictEqual(socket.bytesWritten, N); + })); + assert.strictEqual(socket.bytesWritten, N); + })).listen(0, common.mustCall(() => { + const client = net.connect(server.address().port); + client.resume(); + client.on('close', common.mustCall(() => { + assert.strictEqual(client.bytesRead, N); + server.close(); + })); + })); +} + +{ + // Variant 2: Write a string. + const server = net.createServer(common.mustCall((socket) => { + socket.end('a'.repeat(N), common.mustCall(() => { + assert.strictEqual(socket.bytesWritten, N); + })); + assert.strictEqual(socket.bytesWritten, N); + })).listen(0, common.mustCall(() => { + const client = net.connect(server.address().port); + client.resume(); + client.on('close', common.mustCall(() => { + assert.strictEqual(client.bytesRead, N); + server.close(); + })); + })); +} + +{ + // Variant 2: writev() with mixed data. + const server = net.createServer(common.mustCall((socket) => { + socket.cork(); + socket.write('a'.repeat(N)); + assert.strictEqual(socket.bytesWritten, N); + socket.write(Buffer.alloc(N)); + assert.strictEqual(socket.bytesWritten, 2 * N); + socket.end('', common.mustCall(() => { + assert.strictEqual(socket.bytesWritten, 2 * N); + })); + socket.uncork(); + })).listen(0, common.mustCall(() => { + const client = net.connect(server.address().port); + client.resume(); + client.on('close', common.mustCall(() => { + assert.strictEqual(client.bytesRead, 2 * N); + server.close(); + })); + })); +} diff --git a/node/_tools/test/parallel/test-net-can-reset-timeout.js b/node/_tools/test/parallel/test-net-can-reset-timeout.js new file mode 100644 index 000000000000..04f3eb3dd9e6 --- /dev/null +++ b/node/_tools/test/parallel/test-net-can-reset-timeout.js @@ -0,0 +1,64 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +// Ref: https://github.com/nodejs/node-v0.x-archive/issues/481 + +const net = require('net'); + +const server = net.createServer(common.mustCall(function(stream) { + stream.setTimeout(100); + + stream.resume(); + + stream.once('timeout', common.mustCall(function() { + console.log('timeout'); + // Try to reset the timeout. + stream.write('WHAT.'); + })); + + stream.on('end', common.mustCall(function() { + console.log('server side end'); + stream.end(); + })); +})); + +server.listen(0, common.mustCall(function() { + const c = net.createConnection(this.address().port); + + c.on('data', function() { + c.end(); + }); + + c.on('end', function() { + console.log('client side end'); + server.close(); + }); +})); diff --git a/node/_tools/test/parallel/test-net-connect-after-destroy.js b/node/_tools/test/parallel/test-net-connect-after-destroy.js new file mode 100644 index 000000000000..5c35a9851d14 --- /dev/null +++ b/node/_tools/test/parallel/test-net-connect-after-destroy.js @@ -0,0 +1,16 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +// Regression test for https://github.com/nodejs/node-v0.x-archive/issues/819. + +require('../common'); +const net = require('net'); + +// Connect to something that we need to DNS resolve +const c = net.createConnection(80, 'google.com'); +c.destroy(); diff --git a/node/_tools/test/parallel/test-net-connect-buffer.js b/node/_tools/test/parallel/test-net-connect-buffer.js new file mode 100644 index 000000000000..04e71247e425 --- /dev/null +++ b/node/_tools/test/parallel/test-net-connect-buffer.js @@ -0,0 +1,86 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +// TODO: support not using "new" +const tcp = new net.Server(common.mustCall((s) => { + tcp.close(); + + let buf = ''; + s.setEncoding('utf8'); + s.on('data', function(d) { + buf += d; + }); + + s.on('end', common.mustCall(function() { + console.error('SERVER: end', buf); + assert.strictEqual(buf, "L'État, c'est moi"); + s.end(); + })); +})); + +tcp.listen(0, common.mustCall(function() { + // TODO: support not using "new" + const socket = new net.Stream({ highWaterMark: 0 }); + + let connected = false; + assert.strictEqual(socket.pending, true); + socket.connect(this.address().port, common.mustCall(() => connected = true)); + + assert.strictEqual(socket.pending, true); + assert.strictEqual(socket.connecting, true); + assert.strictEqual(socket.readyState, 'opening'); + + // Write a string that contains a multi-byte character sequence to test that + // `bytesWritten` is incremented with the # of bytes, not # of characters. + const a = "L'État, c'est "; + const b = 'moi'; + + // We're still connecting at this point so the datagram is first pushed onto + // the connect queue. Make sure that it's not added to `bytesWritten` again + // when the actual write happens. + const r = socket.write(a, common.mustCall((er) => { + console.error('write cb'); + assert.ok(connected); + assert.strictEqual(socket.bytesWritten, Buffer.from(a + b).length); + assert.strictEqual(socket.pending, false); + })); + socket.on('close', common.mustCall(() => { + assert.strictEqual(socket.pending, true); + })); + + assert.strictEqual(socket.bytesWritten, Buffer.from(a).length); + assert.strictEqual(r, false); + socket.end(b); + + assert.strictEqual(socket.readyState, 'opening'); +})); diff --git a/node/_tools/test/parallel/test-net-connect-buffer2.js b/node/_tools/test/parallel/test-net-connect-buffer2.js new file mode 100644 index 000000000000..499f3849f287 --- /dev/null +++ b/node/_tools/test/parallel/test-net-connect-buffer2.js @@ -0,0 +1,63 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const tcp = new net.Server(common.mustCall((s) => { + tcp.close(); + + let buf = ''; + s.setEncoding('utf8'); + s.on('data', function(d) { + buf += d; + }); + + s.on('end', common.mustCall(function() { + console.error('SERVER: end', buf); + assert.strictEqual(buf, "L'État, c'est moi"); + s.end(); + })); +})); + +tcp.listen(0, common.mustCall(function() { + const socket = new net.Stream({ highWaterMark: 0 }); + + let connected = false; + assert.strictEqual(socket.pending, true); + socket.connect(this.address().port, common.mustCall(() => connected = true)); + + assert.strictEqual(socket.pending, true); + assert.strictEqual(socket.connecting, true); + assert.strictEqual(socket.readyState, 'opening'); + + // Write a string that contains a multi-byte character sequence to test that + // `bytesWritten` is incremented with the # of bytes, not # of characters. + const a = "L'État, c'est "; + const b = 'moi'; + + // We're still connecting at this point so the datagram is first pushed onto + // the connect queue. Make sure that it's not added to `bytesWritten` again + // when the actual write happens. + const r = socket.write(a, common.mustCall((er) => { + console.error('write cb'); + assert.ok(connected); + assert.strictEqual(socket.bytesWritten, Buffer.from(a + b).length); + assert.strictEqual(socket.pending, false); + })); + socket.on('close', common.mustCall(() => { + assert.strictEqual(socket.pending, true); + })); + + assert.strictEqual(socket.bytesWritten, Buffer.from(a).length); + assert.strictEqual(r, false); + socket.end(b); + + assert.strictEqual(socket.readyState, 'opening'); +})); diff --git a/node/_tools/test/parallel/test-net-connect-call-socket-connect.js b/node/_tools/test/parallel/test-net-connect-call-socket-connect.js new file mode 100644 index 000000000000..1de1ae4519ad --- /dev/null +++ b/node/_tools/test/parallel/test-net-connect-call-socket-connect.js @@ -0,0 +1,46 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); + +// This test checks that calling `net.connect` internally calls +// `Socket.prototype.connect`. +// +// This is important for people who monkey-patch `Socket.prototype.connect` +// since it's not possible to monkey-patch `net.connect` directly (as the core +// `connect` function is called internally in Node instead of calling the +// `exports.connect` function). +// +// Monkey-patching of `Socket.prototype.connect` is done by - among others - +// most APM vendors, the async-listener module and the +// continuation-local-storage module. +// +// Related: +// - https://github.com/nodejs/node/pull/12342 +// - https://github.com/nodejs/node/pull/12852 + +const net = require('net'); +const Socket = net.Socket; + +// Monkey patch Socket.prototype.connect to check that it's called. +const orig = Socket.prototype.connect; +Socket.prototype.connect = common.mustCall(function() { + return orig.apply(this, arguments); +}); + +const server = net.createServer(); + +server.listen(common.mustCall(function() { + const port = server.address().port; + const client = net.connect({ port }, common.mustCall(function() { + client.end(); + })); + client.on('end', common.mustCall(function() { + server.close(); + })); +})); diff --git a/node/_tools/test/parallel/test-net-connect-destroy.js b/node/_tools/test/parallel/test-net-connect-destroy.js new file mode 100644 index 000000000000..a024a032d7ab --- /dev/null +++ b/node/_tools/test/parallel/test-net-connect-destroy.js @@ -0,0 +1,14 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const net = require('net'); + +const socket = new net.Socket(); +socket.on('close', common.mustCall()); +socket.destroy(); diff --git a/node/_tools/test/parallel/test-net-connect-immediate-destroy.js b/node/_tools/test/parallel/test-net-connect-immediate-destroy.js new file mode 100644 index 000000000000..7cc28d6b3ae3 --- /dev/null +++ b/node/_tools/test/parallel/test-net-connect-immediate-destroy.js @@ -0,0 +1,18 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const net = require('net'); + +const server = net.createServer(); +server.listen(0); +const port = server.address().port; +const socket = net.connect(port, common.localhostIPv4, common.mustNotCall()); +socket.on('error', common.mustNotCall()); +server.close(); +socket.destroy(); diff --git a/node/_tools/test/parallel/test-net-connect-immediate-finish.js b/node/_tools/test/parallel/test-net-connect-immediate-finish.js new file mode 100644 index 000000000000..6e5b63ab2889 --- /dev/null +++ b/node/_tools/test/parallel/test-net-connect-immediate-finish.js @@ -0,0 +1,66 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +// This tests that if the socket is still in the 'connecting' state +// when the user calls socket.end() ('finish'), the socket would emit +// 'connect' and defer the handling until the 'connect' event is handled. + +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const { addresses } = require('../common/internet'); +const { + errorLookupMock, + mockedErrorCode, + mockedSysCall +} = require('../common/dns'); + +const client = net.connect({ + host: addresses.INVALID_HOST, + port: 80, // Port number doesn't matter because host name is invalid + lookup: common.mustCall(errorLookupMock()) +}, common.mustNotCall()); + +client.once('error', common.mustCall((error) => { + // TODO(BridgeAR): Add a better way to handle not defined properties using + // `assert.throws(fn, object)`. + assert.ok(!('port' in error)); + assert.ok(!('host' in error)); + assert.throws(() => { throw error; }, { + code: mockedErrorCode, + errno: mockedErrorCode, + syscall: mockedSysCall, + hostname: addresses.INVALID_HOST, + message: 'getaddrinfo ENOTFOUND something.invalid' + }); +})); + +client.end(); diff --git a/node/_tools/test/parallel/test-net-connect-no-arg.js b/node/_tools/test/parallel/test-net-connect-no-arg.js new file mode 100644 index 000000000000..35b5250d049a --- /dev/null +++ b/node/_tools/test/parallel/test-net-connect-no-arg.js @@ -0,0 +1,42 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +require('../common'); +const assert = require('assert'); +const net = require('net'); + +// Tests that net.connect() called without arguments throws ERR_MISSING_ARGS. + +assert.throws(() => { + net.connect(); +}, { + code: 'ERR_MISSING_ARGS', + message: 'The "options" or "port" or "path" argument must be specified', +}); + +assert.throws(() => { + new net.Socket().connect(); +}, { + code: 'ERR_MISSING_ARGS', + message: 'The "options" or "port" or "path" argument must be specified', +}); + +assert.throws(() => { + net.connect({}); +}, { + code: 'ERR_MISSING_ARGS', + message: 'The "options" or "port" or "path" argument must be specified', +}); + +assert.throws(() => { + new net.Socket().connect({}); +}, { + code: 'ERR_MISSING_ARGS', + message: 'The "options" or "port" or "path" argument must be specified', +}); diff --git a/node/_tools/test/parallel/test-net-connect-options-ipv6.js b/node/_tools/test/parallel/test-net-connect-options-ipv6.js new file mode 100644 index 000000000000..e222d4822649 --- /dev/null +++ b/node/_tools/test/parallel/test-net-connect-options-ipv6.js @@ -0,0 +1,74 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// Test that the family option of net.connect is honored. + +'use strict'; +const common = require('../common'); +if (!common.hasIPv6) + common.skip('no IPv6 support'); + +const assert = require('assert'); +const net = require('net'); + +const hostAddrIPv6 = '::1'; +const HOSTNAME = 'dummy'; + +const server = net.createServer({ allowHalfOpen: true }, (socket) => { + socket.resume(); + socket.on('end', common.mustCall()); + socket.end(); +}); + +function tryConnect() { + const connectOpt = { + host: HOSTNAME, + port: server.address().port, + family: 6, + allowHalfOpen: true, + lookup: common.mustCall((addr, opt, cb) => { + assert.strictEqual(addr, HOSTNAME); + assert.strictEqual(opt.family, 6); + cb(null, hostAddrIPv6, opt.family); + }) + }; + // No `mustCall`, since test could skip, and it's the only path to `close`. + const client = net.connect(connectOpt, () => { + client.resume(); + client.on('end', () => { + // Wait for next uv tick and make sure the socket stream is writable. + setTimeout(function() { + assert(client.writable); + client.end(); + }, 10); + }); + client.on('close', () => server.close()); + }); +} + +server.listen(0, hostAddrIPv6, tryConnect); diff --git a/node/_tools/test/parallel/test-net-connect-options-port.js b/node/_tools/test/parallel/test-net-connect-options-port.js new file mode 100644 index 000000000000..4a86a070e4a0 --- /dev/null +++ b/node/_tools/test/parallel/test-net-connect-options-port.js @@ -0,0 +1,225 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const dns = require('dns'); +const net = require('net'); + +// Test wrong type of ports +{ + const portTypeError = { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }; + + syncFailToConnect(true, portTypeError); + syncFailToConnect(false, portTypeError); + syncFailToConnect([], portTypeError, true); + syncFailToConnect({}, portTypeError, true); + syncFailToConnect(null, portTypeError); +} + +// Test out of range ports +{ + const portRangeError = { + code: 'ERR_SOCKET_BAD_PORT', + name: 'RangeError' + }; + + syncFailToConnect('', portRangeError); + syncFailToConnect(' ', portRangeError); + syncFailToConnect('0x', portRangeError, true); + syncFailToConnect('-0x1', portRangeError, true); + syncFailToConnect(NaN, portRangeError); + syncFailToConnect(Infinity, portRangeError); + syncFailToConnect(-1, portRangeError); + syncFailToConnect(65536, portRangeError); +} + +// Test invalid hints +{ + // connect({hint}, cb) and connect({hint}) + const hints = (dns.ADDRCONFIG | dns.V4MAPPED | dns.ALL) + 42; + const hintOptBlocks = doConnect([{ port: 42, hints }], + () => common.mustNotCall()); + for (const fn of hintOptBlocks) { + assert.throws(fn, { + code: 'ERR_INVALID_ARG_VALUE', + name: 'TypeError', + message: /The argument 'hints' is invalid\. Received \d+/ + }); + } +} + +// Test valid combinations of connect(port) and connect(port, host) +{ + const expectedConnections = 72; + let serverConnected = 0; + + const server = net.createServer(common.mustCall((socket) => { + socket.end('ok'); + if (++serverConnected === expectedConnections) { + server.close(); + } + }, expectedConnections)); + + server.listen(0, 'localhost', common.mustCall(() => { + const port = server.address().port; + + // Total connections = 3 * 4(canConnect) * 6(doConnect) = 72 + canConnect(port); + canConnect(String(port)); + canConnect(`0x${port.toString(16)}`); + })); + + // Try connecting to random ports, but do so once the server is closed + server.on('close', () => { + asyncFailToConnect(0); + }); +} + +function doConnect(args, getCb) { + return [ + function createConnectionWithCb() { + return net.createConnection.apply(net, args.concat(getCb())) + .resume(); + }, + function createConnectionWithoutCb() { + return net.createConnection.apply(net, args) + .on('connect', getCb()) + .resume(); + }, + function connectWithCb() { + return net.connect.apply(net, args.concat(getCb())) + .resume(); + }, + function connectWithoutCb() { + return net.connect.apply(net, args) + .on('connect', getCb()) + .resume(); + }, + function socketConnectWithCb() { + const socket = new net.Socket(); + return socket.connect.apply(socket, args.concat(getCb())) + .resume(); + }, + function socketConnectWithoutCb() { + const socket = new net.Socket(); + return socket.connect.apply(socket, args) + .on('connect', getCb()) + .resume(); + }, + ]; +} + +function syncFailToConnect(port, assertErr, optOnly) { + if (!optOnly) { + // connect(port, cb) and connect(port) + const portArgFunctions = doConnect([port], () => common.mustNotCall()); + for (const fn of portArgFunctions) { + assert.throws(fn, assertErr, `${fn.name}(${port})`); + } + + // connect(port, host, cb) and connect(port, host) + const portHostArgFunctions = doConnect([port, 'localhost'], + () => common.mustNotCall()); + for (const fn of portHostArgFunctions) { + assert.throws(fn, assertErr, `${fn.name}(${port}, 'localhost')`); + } + } + // connect({port}, cb) and connect({port}) + const portOptFunctions = doConnect([{ port }], () => common.mustNotCall()); + for (const fn of portOptFunctions) { + assert.throws(fn, assertErr, `${fn.name}({port: ${port}})`); + } + + // connect({port, host}, cb) and connect({port, host}) + const portHostOptFunctions = doConnect([{ port: port, host: 'localhost' }], + () => common.mustNotCall()); + for (const fn of portHostOptFunctions) { + assert.throws(fn, + assertErr, + `${fn.name}({port: ${port}, host: 'localhost'})`); + } +} + +function canConnect(port) { + const noop = () => common.mustCall(); + + // connect(port, cb) and connect(port) + const portArgFunctions = doConnect([port], noop); + for (const fn of portArgFunctions) { + fn(); + } + + // connect(port, host, cb) and connect(port, host) + const portHostArgFunctions = doConnect([port, 'localhost'], noop); + for (const fn of portHostArgFunctions) { + fn(); + } + + // connect({port}, cb) and connect({port}) + const portOptFunctions = doConnect([{ port }], noop); + for (const fn of portOptFunctions) { + fn(); + } + + // connect({port, host}, cb) and connect({port, host}) + const portHostOptFns = doConnect([{ port, host: 'localhost' }], noop); + for (const fn of portHostOptFns) { + fn(); + } +} + +function asyncFailToConnect(port) { + const onError = () => common.mustCall((err) => { + const regexp = /^Error: connect E\w+.+$/; + assert.match(String(err), regexp); + }); + + const dont = () => common.mustNotCall(); + // connect(port, cb) and connect(port) + const portArgFunctions = doConnect([port], dont); + for (const fn of portArgFunctions) { + fn().on('error', onError()); + } + + // connect({port}, cb) and connect({port}) + const portOptFunctions = doConnect([{ port }], dont); + for (const fn of portOptFunctions) { + fn().on('error', onError()); + } + + // connect({port, host}, cb) and connect({port, host}) + const portHostOptFns = doConnect([{ port, host: 'localhost' }], dont); + for (const fn of portHostOptFns) { + fn().on('error', onError()); + } +} diff --git a/node/_tools/test/parallel/test-net-dns-custom-lookup.js b/node/_tools/test/parallel/test-net-dns-custom-lookup.js new file mode 100644 index 000000000000..8397bf831a10 --- /dev/null +++ b/node/_tools/test/parallel/test-net-dns-custom-lookup.js @@ -0,0 +1,61 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +function check(addressType, cb) { + const server = net.createServer(function(client) { + client.end(); + server.close(); + cb && cb(); + }); + + const address = addressType === 4 ? common.localhostIPv4 : '::1'; + server.listen(0, address, common.mustCall(function() { + net.connect({ + port: this.address().port, + host: 'localhost', + family: addressType, + lookup: lookup + }).on('lookup', common.mustCall(function(err, ip, type) { + assert.strictEqual(err, null); + assert.strictEqual(address, ip); + assert.strictEqual(type, addressType); + })); + })); + + function lookup(host, dnsopts, cb) { + dnsopts.family = addressType; + if (addressType === 4) { + process.nextTick(function() { + cb(null, common.localhostIPv4, 4); + }); + } else { + process.nextTick(function() { + cb(null, '::1', 6); + }); + } + } +} + +check(4, function() { + common.hasIPv6 && check(6); +}); + +// Verify that bad lookup() IPs are handled. +{ + net.connect({ + host: 'localhost', + port: 80, + lookup(host, dnsopts, cb) { + cb(null, undefined, 4); + } + }).on('error', common.expectsError({ code: 'ERR_INVALID_IP_ADDRESS' })); +} diff --git a/node/_tools/test/parallel/test-net-dns-error.js b/node/_tools/test/parallel/test-net-dns-error.js new file mode 100644 index 000000000000..2744466ceac8 --- /dev/null +++ b/node/_tools/test/parallel/test-net-dns-error.js @@ -0,0 +1,48 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +const assert = require('assert'); +const net = require('net'); + +const host = '*'.repeat(64); +// Resolving hostname > 63 characters may return EAI_FAIL (permanent failure). +const errCodes = ['ENOTFOUND', 'EAI_FAIL']; + +const socket = net.connect(42, host, common.mustNotCall()); +socket.on('error', common.mustCall(function(err) { + assert(errCodes.includes(err.code), err); +})); +socket.on('lookup', common.mustCall(function(err, ip, type) { + assert(err instanceof Error); + assert(errCodes.includes(err.code), err); + assert.strictEqual(ip, undefined); + assert.strictEqual(type, undefined); +})); diff --git a/node/_tools/test/parallel/test-net-dns-lookup-skip.js b/node/_tools/test/parallel/test-net-dns-lookup-skip.js new file mode 100644 index 000000000000..21962ca4ddcd --- /dev/null +++ b/node/_tools/test/parallel/test-net-dns-lookup-skip.js @@ -0,0 +1,26 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const net = require('net'); + +function check(addressType) { + const server = net.createServer(function(client) { + client.end(); + server.close(); + }); + + const address = addressType === 4 ? '127.0.0.1' : '::1'; + server.listen(0, address, function() { + net.connect(this.address().port, address) + .on('lookup', common.mustNotCall()); + }); +} + +check(4); +common.hasIPv6 && check(6); diff --git a/node/_tools/test/parallel/test-net-dns-lookup.js b/node/_tools/test/parallel/test-net-dns-lookup.js new file mode 100644 index 000000000000..fa06e9f277c5 --- /dev/null +++ b/node/_tools/test/parallel/test-net-dns-lookup.js @@ -0,0 +1,47 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const server = net.createServer(function(client) { + client.end(); + server.close(); +}); + +server.listen(0, '127.0.0.1', common.mustCall(function() { + net.connect(this.address().port, 'localhost') + .on('lookup', common.mustCall(function(err, ip, type, host) { + assert.strictEqual(err, null); + assert.strictEqual(ip, '127.0.0.1'); + assert.strictEqual(type, 4); + assert.strictEqual(host, 'localhost'); + })); +})); diff --git a/node/_tools/test/parallel/test-net-during-close.js b/node/_tools/test/parallel/test-net-during-close.js new file mode 100644 index 000000000000..114825e2199c --- /dev/null +++ b/node/_tools/test/parallel/test-net-during-close.js @@ -0,0 +1,49 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const net = require('net'); + +const server = net.createServer(function(socket) { + socket.end(); +}); + +server.listen(0, common.mustCall(function() { + /* eslint-disable no-unused-expressions */ + const client = net.createConnection(this.address().port); + server.close(); + // Server connection event has not yet fired client is still attempting to + // connect. Accessing properties should not throw in this case. + client.remoteAddress; + client.remoteFamily; + client.remotePort; + // Exit now, do not wait for the client error event. + process.exit(0); + /* eslint-enable no-unused-expressions */ +})); diff --git a/node/_tools/test/parallel/test-net-eaddrinuse.js b/node/_tools/test/parallel/test-net-eaddrinuse.js new file mode 100644 index 000000000000..efb700714d95 --- /dev/null +++ b/node/_tools/test/parallel/test-net-eaddrinuse.js @@ -0,0 +1,44 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const server1 = net.createServer(function(socket) { +}); +const server2 = net.createServer(function(socket) { +}); +server1.listen(0, common.mustCall(function() { + server2.on('error', function(error) { + assert.strictEqual(error.message.includes('EADDRINUSE'), true); + server1.close(); + }); + server2.listen(this.address().port); +})); diff --git a/node/_tools/test/parallel/test-net-end-close.js b/node/_tools/test/parallel/test-net-end-close.js new file mode 100644 index 000000000000..a818dd0974cf --- /dev/null +++ b/node/_tools/test/parallel/test-net-end-close.js @@ -0,0 +1,44 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --expose-internals +'use strict'; +require('../common'); +const assert = require('assert'); +const net = require('net'); + +// const { internalBinding } = require('internal/test/binding'); +// const { UV_EOF } = internalBinding('uv'); +// const { streamBaseState, kReadBytesOrError } = internalBinding('stream_wrap'); + +const s = new net.Socket({ + handle: { + readStart: function() { + setImmediate(() => { + // streamBaseState[kReadBytesOrError] = UV_EOF; + // internal onread has different shape to Node. + this.onread(new Uint8Array(), -4095); + }); + }, + close: (cb) => setImmediate(cb) + }, + writable: false +}); +assert.strictEqual(s, s.resume()); + +const events = []; + +s.on('end', () => { + events.push('end'); +}); +s.on('close', () => { + events.push('close'); +}); + +process.on('exit', () => { + assert.deepStrictEqual(events, [ 'end', 'close' ]); +}); diff --git a/node/_tools/test/parallel/test-net-end-destroyed.js b/node/_tools/test/parallel/test-net-end-destroyed.js new file mode 100644 index 000000000000..786fb3e21e24 --- /dev/null +++ b/node/_tools/test/parallel/test-net-end-destroyed.js @@ -0,0 +1,33 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const net = require('net'); +const assert = require('assert'); + +const server = net.createServer(); + +server.on('connection', common.mustCall()); + +// Ensure that the socket is not destroyed when the 'end' event is emitted. + +server.listen(common.mustCall(function() { + const socket = net.createConnection({ + port: server.address().port + }); + + socket.on('connect', common.mustCall(function() { + socket.on('end', common.mustCall(function() { + assert.strictEqual(socket.destroyed, false); + server.close(); + })); + + socket.end(); + })); +})); diff --git a/node/_tools/test/parallel/test-net-end-without-connect.js b/node/_tools/test/parallel/test-net-end-without-connect.js new file mode 100644 index 000000000000..19997b564bf5 --- /dev/null +++ b/node/_tools/test/parallel/test-net-end-without-connect.js @@ -0,0 +1,34 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const net = require('net'); + +const sock = new net.Socket(); +sock.end(); // Should not throw. diff --git a/node/_tools/test/parallel/test-net-isip.js b/node/_tools/test/parallel/test-net-isip.js new file mode 100644 index 000000000000..a3cc7f2b4c51 --- /dev/null +++ b/node/_tools/test/parallel/test-net-isip.js @@ -0,0 +1,103 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const net = require('net'); + +assert.strictEqual(net.isIP('127.0.0.1'), 4); +assert.strictEqual(net.isIP('x127.0.0.1'), 0); +assert.strictEqual(net.isIP('example.com'), 0); +assert.strictEqual(net.isIP('0000:0000:0000:0000:0000:0000:0000:0000'), 6); +assert.strictEqual(net.isIP('0000:0000:0000:0000:0000:0000:0000:0000::0000'), + 0); +assert.strictEqual(net.isIP('1050:0:0:0:5:600:300c:326b'), 6); +assert.strictEqual(net.isIP('2001:252:0:1::2008:6'), 6); +assert.strictEqual(net.isIP('2001:dead:beef:1::2008:6'), 6); +assert.strictEqual(net.isIP('2001::'), 6); +assert.strictEqual(net.isIP('2001:dead::'), 6); +assert.strictEqual(net.isIP('2001:dead:beef::'), 6); +assert.strictEqual(net.isIP('2001:dead:beef:1::'), 6); +assert.strictEqual(net.isIP('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'), 6); +assert.strictEqual(net.isIP(':2001:252:0:1::2008:6:'), 0); +assert.strictEqual(net.isIP(':2001:252:0:1::2008:6'), 0); +assert.strictEqual(net.isIP('2001:252:0:1::2008:6:'), 0); +assert.strictEqual(net.isIP('2001:252::1::2008:6'), 0); +assert.strictEqual(net.isIP('::2001:252:1:2008:6'), 6); +assert.strictEqual(net.isIP('::2001:252:1:1.1.1.1'), 6); +assert.strictEqual(net.isIP('::2001:252:1:255.255.255.255'), 6); +assert.strictEqual(net.isIP('::2001:252:1:255.255.255.255.76'), 0); +assert.strictEqual(net.isIP('fe80::2008%eth0'), 6); +assert.strictEqual(net.isIP('fe80::2008%eth0.0'), 6); +assert.strictEqual(net.isIP('fe80::2008%eth0@1'), 0); +assert.strictEqual(net.isIP('::anything'), 0); +assert.strictEqual(net.isIP('::1'), 6); +assert.strictEqual(net.isIP('::'), 6); +assert.strictEqual(net.isIP('0000:0000:0000:0000:0000:0000:12345:0000'), 0); +assert.strictEqual(net.isIP('0'), 0); +assert.strictEqual(net.isIP(), 0); +assert.strictEqual(net.isIP(''), 0); +assert.strictEqual(net.isIP(null), 0); +assert.strictEqual(net.isIP(123), 0); +assert.strictEqual(net.isIP(true), 0); +assert.strictEqual(net.isIP({}), 0); +assert.strictEqual(net.isIP({ toString: () => '::2001:252:1:255.255.255.255' }), + 6); +assert.strictEqual(net.isIP({ toString: () => '127.0.0.1' }), 4); +assert.strictEqual(net.isIP({ toString: () => 'bla' }), 0); + +assert.strictEqual(net.isIPv4('127.0.0.1'), true); +assert.strictEqual(net.isIPv4('example.com'), false); +assert.strictEqual(net.isIPv4('2001:252:0:1::2008:6'), false); +assert.strictEqual(net.isIPv4(), false); +assert.strictEqual(net.isIPv4(''), false); +assert.strictEqual(net.isIPv4(null), false); +assert.strictEqual(net.isIPv4(123), false); +assert.strictEqual(net.isIPv4(true), false); +assert.strictEqual(net.isIPv4({}), false); +assert.strictEqual(net.isIPv4({ + toString: () => '::2001:252:1:255.255.255.255' +}), false); +assert.strictEqual(net.isIPv4({ toString: () => '127.0.0.1' }), true); +assert.strictEqual(net.isIPv4({ toString: () => 'bla' }), false); + +assert.strictEqual(net.isIPv6('127.0.0.1'), false); +assert.strictEqual(net.isIPv6('example.com'), false); +assert.strictEqual(net.isIPv6('2001:252:0:1::2008:6'), true); +assert.strictEqual(net.isIPv6(), false); +assert.strictEqual(net.isIPv6(''), false); +assert.strictEqual(net.isIPv6(null), false); +assert.strictEqual(net.isIPv6(123), false); +assert.strictEqual(net.isIPv6(true), false); +assert.strictEqual(net.isIPv6({}), false); +assert.strictEqual(net.isIPv6({ + toString: () => '::2001:252:1:255.255.255.255' +}), true); +assert.strictEqual(net.isIPv6({ toString: () => '127.0.0.1' }), false); +assert.strictEqual(net.isIPv6({ toString: () => 'bla' }), false); diff --git a/node/_tools/test/parallel/test-net-isipv4.js b/node/_tools/test/parallel/test-net-isipv4.js new file mode 100644 index 000000000000..21b9cafba649 --- /dev/null +++ b/node/_tools/test/parallel/test-net-isipv4.js @@ -0,0 +1,53 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const assert = require('assert'); +const net = require('net'); + +const v4 = [ + '0.0.0.0', + '8.8.8.8', + '127.0.0.1', + '100.100.100.100', + '192.168.0.1', + '18.101.25.153', + '123.23.34.2', + '172.26.168.134', + '212.58.241.131', + '128.0.0.0', + '23.71.254.72', + '223.255.255.255', + '192.0.2.235', + '99.198.122.146', + '46.51.197.88', + '173.194.34.134', +]; + +const v4not = [ + '.100.100.100.100', + '100..100.100.100.', + '100.100.100.100.', + '999.999.999.999', + '256.256.256.256', + '256.100.100.100.100', + '123.123.123', + 'http://123.123.123', + '1000.2.3.4', + '999.2.3.4', + '0000000192.168.0.200', + '192.168.0.2000000000', +]; + +v4.forEach((ip) => { + assert.strictEqual(net.isIPv4(ip), true); +}); + +v4not.forEach((ip) => { + assert.strictEqual(net.isIPv4(ip), false); +}); diff --git a/node/_tools/test/parallel/test-net-isipv6.js b/node/_tools/test/parallel/test-net-isipv6.js new file mode 100644 index 000000000000..1b5689cc0ef8 --- /dev/null +++ b/node/_tools/test/parallel/test-net-isipv6.js @@ -0,0 +1,251 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const assert = require('assert'); +const net = require('net'); + +const v6 = [ + '::', + '1::', + '::1', + '1::8', + '1::7:8', + '1:2:3:4:5:6:7:8', + '1:2:3:4:5:6::8', + '1:2:3:4:5:6:7::', + '1:2:3:4:5::7:8', + '1:2:3:4:5::8', + '1:2:3::8', + '1::4:5:6:7:8', + '1::6:7:8', + '1::3:4:5:6:7:8', + '1:2:3:4::6:7:8', + '1:2::4:5:6:7:8', + '::2:3:4:5:6:7:8', + '1:2::8', + '2001:0000:1234:0000:0000:C1C0:ABCD:0876', + '3ffe:0b00:0000:0000:0001:0000:0000:000a', + 'FF02:0000:0000:0000:0000:0000:0000:0001', + '0000:0000:0000:0000:0000:0000:0000:0001', + '0000:0000:0000:0000:0000:0000:0000:0000', + '::ffff:192.168.1.26', + '2::10', + 'ff02::1', + 'fe80::', + '2002::', + '2001:db8::', + '2001:0db8:1234::', + '::ffff:0:0', + '::ffff:192.168.1.1', + '1:2:3:4::8', + '1::2:3:4:5:6:7', + '1::2:3:4:5:6', + '1::2:3:4:5', + '1::2:3:4', + '1::2:3', + '::2:3:4:5:6:7', + '::2:3:4:5:6', + '::2:3:4:5', + '::2:3:4', + '::2:3', + '::8', + '1:2:3:4:5:6::', + '1:2:3:4:5::', + '1:2:3:4::', + '1:2:3::', + '1:2::', + '1:2:3:4::7:8', + '1:2:3::7:8', + '1:2::7:8', + '1:2:3:4:5:6:1.2.3.4', + '1:2:3:4:5::1.2.3.4', + '1:2:3:4::1.2.3.4', + '1:2:3::1.2.3.4', + '1:2::1.2.3.4', + '1::1.2.3.4', + '1:2:3:4::5:1.2.3.4', + '1:2:3::5:1.2.3.4', + '1:2::5:1.2.3.4', + '1::5:1.2.3.4', + '1::5:11.22.33.44', + 'fe80::217:f2ff:254.7.237.98', + 'fe80::217:f2ff:fe07:ed62', + '2001:DB8:0:0:8:800:200C:417A', + 'FF01:0:0:0:0:0:0:101', + '0:0:0:0:0:0:0:1', + '0:0:0:0:0:0:0:0', + '2001:DB8::8:800:200C:417A', + 'FF01::101', + '0:0:0:0:0:0:13.1.68.3', + '0:0:0:0:0:FFFF:129.144.52.38', + '::13.1.68.3', + '::FFFF:129.144.52.38', + 'fe80:0000:0000:0000:0204:61ff:fe9d:f156', + 'fe80:0:0:0:204:61ff:fe9d:f156', + 'fe80::204:61ff:fe9d:f156', + 'fe80:0:0:0:204:61ff:254.157.241.86', + 'fe80::204:61ff:254.157.241.86', + 'fe80::1', + '2001:0db8:85a3:0000:0000:8a2e:0370:7334', + '2001:db8:85a3:0:0:8a2e:370:7334', + '2001:db8:85a3::8a2e:370:7334', + '2001:0db8:0000:0000:0000:0000:1428:57ab', + '2001:0db8:0000:0000:0000::1428:57ab', + '2001:0db8:0:0:0:0:1428:57ab', + '2001:0db8:0:0::1428:57ab', + '2001:0db8::1428:57ab', + '2001:db8::1428:57ab', + '::ffff:12.34.56.78', + '::ffff:0c22:384e', + '2001:0db8:1234:0000:0000:0000:0000:0000', + '2001:0db8:1234:ffff:ffff:ffff:ffff:ffff', + '2001:db8:a::123', + '::ffff:192.0.2.128', + '::ffff:c000:280', + 'a:b:c:d:e:f:f1:f2', + 'a:b:c::d:e:f:f1', + 'a:b:c::d:e:f', + 'a:b:c::d:e', + 'a:b:c::d', + '::a', + '::a:b:c', + '::a:b:c:d:e:f:f1', + 'a::', + 'a:b:c::', + 'a:b:c:d:e:f:f1::', + 'a:bb:ccc:dddd:000e:00f:0f::', + '0:a:0:a:0:0:0:a', + '0:a:0:0:a:0:0:a', + '2001:db8:1:1:1:1:0:0', + '2001:db8:1:1:1:0:0:0', + '2001:db8:1:1:0:0:0:0', + '2001:db8:1:0:0:0:0:0', + '2001:db8:0:0:0:0:0:0', + '2001:0:0:0:0:0:0:0', + 'A:BB:CCC:DDDD:000E:00F:0F::', + '0:0:0:0:0:0:0:a', + '0:0:0:0:a:0:0:0', + '0:0:0:a:0:0:0:0', + 'a:0:0:a:0:0:a:a', + 'a:0:0:a:0:0:0:a', + 'a:0:0:0:a:0:0:a', + 'a:0:0:0:a:0:0:0', + 'a:0:0:0:0:0:0:0', + 'fe80::7:8%eth0', + 'fe80::7:8%1', +]; + +const v6not = [ + '', + '1:', + ':1', + '11:36:12', + '02001:0000:1234:0000:0000:C1C0:ABCD:0876', + '2001:0000:1234:0000:00001:C1C0:ABCD:0876', + '2001:0000:1234: 0000:0000:C1C0:ABCD:0876', + '2001:1:1:1:1:1:255Z255X255Y255', + '3ffe:0b00:0000:0001:0000:0000:000a', + 'FF02:0000:0000:0000:0000:0000:0000:0000:0001', + '3ffe:b00::1::a', + '::1111:2222:3333:4444:5555:6666::', + '1:2:3::4:5::7:8', + '12345::6:7:8', + '1::5:400.2.3.4', + '1::5:260.2.3.4', + '1::5:256.2.3.4', + '1::5:1.256.3.4', + '1::5:1.2.256.4', + '1::5:1.2.3.256', + '1::5:300.2.3.4', + '1::5:1.300.3.4', + '1::5:1.2.300.4', + '1::5:1.2.3.300', + '1::5:900.2.3.4', + '1::5:1.900.3.4', + '1::5:1.2.900.4', + '1::5:1.2.3.900', + '1::5:300.300.300.300', + '1::5:3000.30.30.30', + '1::400.2.3.4', + '1::260.2.3.4', + '1::256.2.3.4', + '1::1.256.3.4', + '1::1.2.256.4', + '1::1.2.3.256', + '1::300.2.3.4', + '1::1.300.3.4', + '1::1.2.300.4', + '1::1.2.3.300', + '1::900.2.3.4', + '1::1.900.3.4', + '1::1.2.900.4', + '1::1.2.3.900', + '1::300.300.300.300', + '1::3000.30.30.30', + '::400.2.3.4', + '::260.2.3.4', + '::256.2.3.4', + '::1.256.3.4', + '::1.2.256.4', + '::1.2.3.256', + '::300.2.3.4', + '::1.300.3.4', + '::1.2.300.4', + '::1.2.3.300', + '::900.2.3.4', + '::1.900.3.4', + '::1.2.900.4', + '::1.2.3.900', + '::300.300.300.300', + '::3000.30.30.30', + '2001:DB8:0:0:8:800:200C:417A:221', + 'FF01::101::2', + '1111:2222:3333:4444::5555:', + '1111:2222:3333::5555:', + '1111:2222::5555:', + '1111::5555:', + '::5555:', + ':::', + '1111:', + ':', + ':1111:2222:3333:4444::5555', + ':1111:2222:3333::5555', + ':1111:2222::5555', + ':1111::5555', + ':::5555', + '1.2.3.4:1111:2222:3333:4444::5555', + '1.2.3.4:1111:2222:3333::5555', + '1.2.3.4:1111:2222::5555', + '1.2.3.4:1111::5555', + '1.2.3.4::5555', + '1.2.3.4::', + 'fe80:0000:0000:0000:0204:61ff:254.157.241.086', + '123', + 'ldkfj', + '2001::FFD3::57ab', + '2001:db8:85a3::8a2e:37023:7334', + '2001:db8:85a3::8a2e:370k:7334', + '1:2:3:4:5:6:7:8:9', + '1::2::3', + '1:::3:4:5', + '1:2:3::4:5:6:7:8:9', + '::ffff:2.3.4', + '::ffff:257.1.2.3', + '::ffff:12345678901234567890.1.26', + '2001:0000:1234:0000:0000:C1C0:ABCD:0876 0', + '02001:0000:1234:0000:0000:C1C0:ABCD:0876', +]; + +v6.forEach((ip) => { + assert.strictEqual(net.isIPv6(ip), true); +}); + +v6not.forEach((ip) => { + assert.strictEqual(net.isIPv6(ip), false); +}); diff --git a/node/_tools/test/parallel/test-net-listen-after-destroying-stdin.js b/node/_tools/test/parallel/test-net-listen-after-destroying-stdin.js new file mode 100644 index 000000000000..817ea702ffe3 --- /dev/null +++ b/node/_tools/test/parallel/test-net-listen-after-destroying-stdin.js @@ -0,0 +1,29 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +// Just test that destroying stdin doesn't mess up listening on a server. +// This is a regression test for +// https://github.com/nodejs/node-v0.x-archive/issues/746. + +const common = require('../common'); +const net = require('net'); + +process.stdin.destroy(); + +const server = net.createServer(common.mustCall((socket) => { + console.log('accepted...'); + socket.end(common.mustCall(() => { console.log('finished...'); })); + server.close(common.mustCall(() => { console.log('closed'); })); +})); + + +server.listen(0, common.mustCall(() => { + console.log('listening...'); + + net.createConnection(server.address().port); +})); diff --git a/node/_tools/test/parallel/test-net-listen-close-server-callback-is-not-function.js b/node/_tools/test/parallel/test-net-listen-close-server-callback-is-not-function.js new file mode 100644 index 000000000000..b3cf1240aa7f --- /dev/null +++ b/node/_tools/test/parallel/test-net-listen-close-server-callback-is-not-function.js @@ -0,0 +1,18 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const net = require('net'); + +const server = net.createServer(common.mustNotCall()); + +server.on('close', common.mustCall()); + +server.listen(0, common.mustNotCall()); + +server.close('bad argument'); diff --git a/node/_tools/test/parallel/test-net-listen-close-server.js b/node/_tools/test/parallel/test-net-listen-close-server.js new file mode 100644 index 000000000000..791f142c11d4 --- /dev/null +++ b/node/_tools/test/parallel/test-net-listen-close-server.js @@ -0,0 +1,37 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const net = require('net'); + +const server = net.createServer(function(socket) { +}); +server.listen(0, common.mustNotCall()); +server.on('error', common.mustNotCall()); +server.close(); diff --git a/node/_tools/test/parallel/test-net-listen-error.js b/node/_tools/test/parallel/test-net-listen-error.js new file mode 100644 index 000000000000..bee18903d19b --- /dev/null +++ b/node/_tools/test/parallel/test-net-listen-error.js @@ -0,0 +1,36 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const net = require('net'); + +const server = net.createServer(function(socket) { +}); +server.listen(1, '1.1.1.1', common.mustNotCall()); // EACCES or EADDRNOTAVAIL +server.on('error', common.mustCall()); diff --git a/node/_tools/test/parallel/test-net-listen-invalid-port.js b/node/_tools/test/parallel/test-net-listen-invalid-port.js new file mode 100644 index 000000000000..10685f1d0ffe --- /dev/null +++ b/node/_tools/test/parallel/test-net-listen-invalid-port.js @@ -0,0 +1,52 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); + +// This test ensures that port numbers are validated in *all* kinds of `listen` +// calls. If an invalid port is supplied, ensures a `RangeError` is thrown. +// https://github.com/nodejs/node/issues/5727 + +const assert = require('assert'); +const net = require('net'); + +const invalidPort = -1 >>> 0; + +// TODO: support net.Server() without new + +new net.Server().listen(0, function() { + const address = this.address(); + const key = `${address.family}:${address.address}:0`; + + assert.strictEqual(this._connectionKey, key); + this.close(); +}); + +// The first argument is a configuration object +assert.throws(() => { + new net.Server().listen({ port: invalidPort }, common.mustNotCall()); +}, { + code: 'ERR_SOCKET_BAD_PORT', + name: 'RangeError' +}); + +// The first argument is the port, no IP given. +assert.throws(() => { + new net.Server().listen(invalidPort, common.mustNotCall()); +}, { + code: 'ERR_SOCKET_BAD_PORT', + name: 'RangeError' +}); + +// The first argument is the port, the second an IP. +assert.throws(() => { + new net.Server().listen(invalidPort, '0.0.0.0', common.mustNotCall()); +}, { + code: 'ERR_SOCKET_BAD_PORT', + name: 'RangeError' +}); diff --git a/node/_tools/test/parallel/test-net-listening.js b/node/_tools/test/parallel/test-net-listening.js new file mode 100644 index 000000000000..b7eb66474757 --- /dev/null +++ b/node/_tools/test/parallel/test-net-listening.js @@ -0,0 +1,23 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const server = net.createServer(); + +assert.strictEqual(server.listening, false); + +server.listen(0, common.mustCall(() => { + assert.strictEqual(server.listening, true); + + server.close(common.mustCall(() => { + assert.strictEqual(server.listening, false); + })); +})); diff --git a/node/_tools/test/parallel/test-net-local-address-port.js b/node/_tools/test/parallel/test-net-local-address-port.js new file mode 100644 index 000000000000..8dba2a3637c9 --- /dev/null +++ b/node/_tools/test/parallel/test-net-local-address-port.js @@ -0,0 +1,49 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const server = net.createServer(common.mustCall(function(socket) { + assert.strictEqual(socket.localAddress, common.localhostIPv4); + assert.strictEqual(socket.localPort, this.address().port); + socket.on('end', function() { + server.close(); + }); + socket.resume(); +})); + +server.listen(0, common.localhostIPv4, function() { + const client = net.createConnection(this.address() + .port, common.localhostIPv4); + client.on('connect', function() { + client.end(); + }); +}); diff --git a/node/_tools/test/parallel/test-net-localerror.js b/node/_tools/test/parallel/test-net-localerror.js new file mode 100644 index 000000000000..07e74e71116b --- /dev/null +++ b/node/_tools/test/parallel/test-net-localerror.js @@ -0,0 +1,51 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const net = require('net'); + +const connect = (opts, code, type) => { + assert.throws( + () => net.connect(opts), + { code, name: type.name } + ); +}; + +connect({ + host: 'localhost', + port: 0, + localAddress: 'foobar', +}, 'ERR_INVALID_IP_ADDRESS', TypeError); + +connect({ + host: 'localhost', + port: 0, + localPort: 'foobar', +}, 'ERR_INVALID_ARG_TYPE', TypeError); diff --git a/node/_tools/test/parallel/test-net-options-lookup.js b/node/_tools/test/parallel/test-net-options-lookup.js new file mode 100644 index 000000000000..faf938667462 --- /dev/null +++ b/node/_tools/test/parallel/test-net-options-lookup.js @@ -0,0 +1,55 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +['foobar', 1, {}, []].forEach((input) => connectThrows(input)); + +// Using port 0 as lookup is emitted before connecting. +function connectThrows(input) { + const opts = { + host: 'localhost', + port: 0, + lookup: input + }; + + assert.throws(() => { + net.connect(opts); + }, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); +} + +connectDoesNotThrow(() => {}); + +function connectDoesNotThrow(input) { + const opts = { + host: 'localhost', + port: 0, + lookup: input + }; + + return net.connect(opts); +} + +{ + // Verify that an error is emitted when an invalid address family is returned. + const s = connectDoesNotThrow((host, options, cb) => { + cb(null, '127.0.0.1', 100); + }); + + s.on('error', common.expectsError({ + code: 'ERR_INVALID_ADDRESS_FAMILY', + host: 'localhost', + port: 0, + message: 'Invalid address family: 100 localhost:0' + })); +} diff --git a/node/_tools/test/parallel/test-net-pause-resume-connecting.js b/node/_tools/test/parallel/test-net-pause-resume-connecting.js new file mode 100644 index 000000000000..c030c97edaf9 --- /dev/null +++ b/node/_tools/test/parallel/test-net-pause-resume-connecting.js @@ -0,0 +1,102 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +let connections = 0; +let dataEvents = 0; +let conn; + + +// Server +const server = net.createServer(function(conn) { + connections++; + conn.end('This was the year he fell to pieces.'); + + if (connections === 5) + server.close(); +}); + +server.listen(0, function() { + // Client 1 + conn = net.createConnection(this.address().port, 'localhost'); + conn.resume(); + conn.on('data', onDataOk); + + + // Client 2 + conn = net.createConnection(this.address().port, 'localhost'); + conn.pause(); + conn.resume(); + conn.on('data', onDataOk); + + + // Client 3 + conn = net.createConnection(this.address().port, 'localhost'); + conn.pause(); + conn.on('data', common.mustNotCall()); + scheduleTearDown(conn); + + + // Client 4 + conn = net.createConnection(this.address().port, 'localhost'); + conn.resume(); + conn.pause(); + conn.resume(); + conn.on('data', onDataOk); + + + // Client 5 + conn = net.createConnection(this.address().port, 'localhost'); + conn.resume(); + conn.resume(); + conn.pause(); + conn.on('data', common.mustNotCall()); + scheduleTearDown(conn); + + function onDataOk() { + dataEvents++; + } + + function scheduleTearDown(conn) { + setTimeout(function() { + conn.removeAllListeners('data'); + conn.resume(); + }, 100); + } +}); + + +// Exit sanity checks +process.on('exit', function() { + assert.strictEqual(connections, 5); + assert.strictEqual(dataEvents, 3); +}); diff --git a/node/_tools/test/parallel/test-net-persistent-ref-unref.js b/node/_tools/test/parallel/test-net-persistent-ref-unref.js new file mode 100644 index 000000000000..4152e77315ab --- /dev/null +++ b/node/_tools/test/parallel/test-net-persistent-ref-unref.js @@ -0,0 +1,48 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --expose-internals +'use strict'; +require('../common'); +const assert = require('assert'); +const net = require('net'); +const { internalBinding } = require('internal/test/binding'); +const TCPWrap = internalBinding('tcp_wrap').TCP; + +const echoServer = net.createServer((conn) => { + conn.end(); +}); + +const ref = TCPWrap.prototype.ref; +const unref = TCPWrap.prototype.unref; + +let refCount = 0; + +TCPWrap.prototype.ref = function() { + ref.call(this); + refCount++; + assert.strictEqual(refCount, 0); +}; + +TCPWrap.prototype.unref = function() { + unref.call(this); + refCount--; + assert.strictEqual(refCount, -1); +}; + +echoServer.listen(0); + +echoServer.on('listening', function() { + const sock = new net.Socket(); + sock.unref(); + sock.ref(); + sock.connect(this.address().port); + sock.on('end', () => { + assert.strictEqual(refCount, 0); + echoServer.close(); + }); +}); diff --git a/node/_tools/test/parallel/test-net-pipe-connect-errors.js b/node/_tools/test/parallel/test-net-pipe-connect-errors.js new file mode 100644 index 000000000000..7ad19f885a46 --- /dev/null +++ b/node/_tools/test/parallel/test-net-pipe-connect-errors.js @@ -0,0 +1,104 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const fs = require('fs'); +const net = require('net'); +const assert = require('assert'); + +// Test if ENOTSOCK is fired when trying to connect to a file which is not +// a socket. + +let emptyTxt; + +if (common.isWindows) { + // On Win, common.PIPE will be a named pipe, so we use an existing empty + // file instead + emptyTxt = fixtures.path('empty.txt'); +} else { + const tmpdir = require('../common/tmpdir'); + tmpdir.refresh(); + // Keep the file name very short so that we don't exceed the 108 char limit + // on CI for a POSIX socket. Even though this isn't actually a socket file, + // the error will be different from the one we are expecting if we exceed the + // limit. + emptyTxt = `${tmpdir.path}0.txt`; + + function cleanup() { + try { + fs.unlinkSync(emptyTxt); + } catch (e) { + assert.strictEqual(e.code, 'ENOENT'); + } + } + process.on('exit', cleanup); + cleanup(); + fs.writeFileSync(emptyTxt, ''); +} + +const notSocketClient = net.createConnection(emptyTxt, function() { + assert.fail('connection callback should not run'); +}); + +notSocketClient.on('error', common.mustCall(function(err) { + assert(err.code === 'ENOTSOCK' || err.code === 'ECONNREFUSED', + `received ${err.code} instead of ENOTSOCK or ECONNREFUSED`); +})); + + +// Trying to connect to not-existing socket should result in ENOENT error +const noEntSocketClient = net.createConnection('no-ent-file', function() { + assert.fail('connection to non-existent socket, callback should not run'); +}); + +noEntSocketClient.on('error', common.mustCall(function(err) { + assert.strictEqual(err.code, 'ENOENT'); +})); + + +// On Windows or IBMi or when running as root, +// a chmod has no effect on named pipes +if (!common.isWindows && !common.isIBMi && process.getuid() !== 0) { + // Trying to connect to a socket one has no access to should result in EACCES + const accessServer = net.createServer( + common.mustNotCall('server callback should not run')); + accessServer.listen(common.PIPE, common.mustCall(function() { + fs.chmodSync(common.PIPE, 0); + + const accessClient = net.createConnection(common.PIPE, function() { + assert.fail('connection should get EACCES, callback should not run'); + }); + + accessClient.on('error', common.mustCall(function(err) { + assert.strictEqual(err.code, 'EACCES'); + accessServer.close(); + })); + })); +} diff --git a/node/_tools/test/parallel/test-net-remote-address-port.js b/node/_tools/test/parallel/test-net-remote-address-port.js new file mode 100644 index 000000000000..3c4697a0388c --- /dev/null +++ b/node/_tools/test/parallel/test-net-remote-address-port.js @@ -0,0 +1,81 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const net = require('net'); + +let conns_closed = 0; + +const remoteAddrCandidates = [ common.localhostIPv4 ]; +if (common.hasIPv6) remoteAddrCandidates.push('::ffff:127.0.0.1'); + +const remoteFamilyCandidates = ['IPv4']; +if (common.hasIPv6) remoteFamilyCandidates.push('IPv6'); + +const server = net.createServer(common.mustCall(function(socket) { + assert.ok(remoteAddrCandidates.includes(socket.remoteAddress)); + assert.ok(remoteFamilyCandidates.includes(socket.remoteFamily)); + assert.ok(socket.remotePort); + assert.notStrictEqual(socket.remotePort, this.address().port); + socket.on('end', function() { + if (++conns_closed === 2) server.close(); + }); + socket.on('close', function() { + assert.ok(remoteAddrCandidates.includes(socket.remoteAddress)); + assert.ok(remoteFamilyCandidates.includes(socket.remoteFamily)); + }); + socket.resume(); +}, 2)); + +server.listen(0, 'localhost', function() { + const client = net.createConnection(this.address().port, 'localhost'); + const client2 = net.createConnection(this.address().port); + client.on('connect', function() { + assert.ok(remoteAddrCandidates.includes(client.remoteAddress)); + assert.ok(remoteFamilyCandidates.includes(client.remoteFamily)); + assert.strictEqual(client.remotePort, server.address().port); + client.end(); + }); + client.on('close', function() { + assert.ok(remoteAddrCandidates.includes(client.remoteAddress)); + assert.ok(remoteFamilyCandidates.includes(client.remoteFamily)); + }); + client2.on('connect', function() { + assert.ok(remoteAddrCandidates.includes(client2.remoteAddress)); + assert.ok(remoteFamilyCandidates.includes(client2.remoteFamily)); + assert.strictEqual(client2.remotePort, server.address().port); + client2.end(); + }); + client2.on('close', function() { + assert.ok(remoteAddrCandidates.includes(client2.remoteAddress)); + assert.ok(remoteFamilyCandidates.includes(client2.remoteFamily)); + }); +}); diff --git a/node/_tools/test/parallel/test-net-server-call-listen-multiple-times.js b/node/_tools/test/parallel/test-net-server-call-listen-multiple-times.js new file mode 100644 index 000000000000..30b443b1806f --- /dev/null +++ b/node/_tools/test/parallel/test-net-server-call-listen-multiple-times.js @@ -0,0 +1,56 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +// TODO: support net.Server() without new + +// First test. Check that after error event you can listen right away. +{ + const dummyServer = new net.Server(); + const server = new net.Server(); + + // Run some server in order to simulate EADDRINUSE error. + dummyServer.listen(common.mustCall(() => { + // Try to listen used port. + server.listen(dummyServer.address().port); + })); + + server.on('error', common.mustCall((e) => { + server.listen(common.mustCall(() => { + dummyServer.close(); + server.close(); + })); + })); +} + +// Second test. Check that second listen call throws an error. +{ + const server = new net.Server(); + + server.listen(common.mustCall(() => server.close())); + + assert.throws(() => server.listen(), { + code: 'ERR_SERVER_ALREADY_LISTEN', + name: 'Error' + }); +} + +// Third test. +// Check that after the close call you can run listen method just fine. +{ + const server = new net.Server(); + + server.listen(common.mustCall(() => { + server.close(); + server.listen(common.mustCall(() => server.close())); + })); +} diff --git a/node/_tools/test/parallel/test-net-server-capture-rejection.js b/node/_tools/test/parallel/test-net-server-capture-rejection.js new file mode 100644 index 000000000000..012a0abb4480 --- /dev/null +++ b/node/_tools/test/parallel/test-net-server-capture-rejection.js @@ -0,0 +1,34 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const events = require('events'); +const { createServer, connect } = require('net'); + +events.captureRejections = true; + +const server = createServer(common.mustCall(async (sock) => { + server.close(); + + const _err = new Error('kaboom'); + sock.on('error', common.mustCall((err) => { + assert.strictEqual(err, _err); + })); + throw _err; +})); + +server.listen(0, common.mustCall(() => { + const sock = connect( + server.address().port, + server.address().host + ); + + sock.on('close', common.mustCall()); +})); diff --git a/node/_tools/test/parallel/test-net-server-close.js b/node/_tools/test/parallel/test-net-server-close.js new file mode 100644 index 000000000000..a855d8740753 --- /dev/null +++ b/node/_tools/test/parallel/test-net-server-close.js @@ -0,0 +1,52 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const sockets = []; + +const server = net.createServer(function(c) { + c.on('close', common.mustCall()); + + sockets.push(c); + + if (sockets.length === 2) { + assert.strictEqual(server.close(), server); + sockets.forEach((c) => c.destroy()); + } +}); + +server.on('close', common.mustCall()); + +assert.strictEqual(server, server.listen(0, () => { + net.createConnection(server.address().port); + net.createConnection(server.address().port); +})); diff --git a/node/_tools/test/parallel/test-net-server-listen-options-signal.js b/node/_tools/test/parallel/test-net-server-listen-options-signal.js new file mode 100644 index 000000000000..01600c5b0634 --- /dev/null +++ b/node/_tools/test/parallel/test-net-server-listen-options-signal.js @@ -0,0 +1,39 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +{ + // Test bad signal. + const server = net.createServer(); + assert.throws( + () => server.listen({ port: 0, signal: 'INVALID_SIGNAL' }), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); +} + +{ + // Test close. + const server = net.createServer(); + const controller = new AbortController(); + server.on('close', common.mustCall()); + server.listen({ port: 0, signal: controller.signal }); + controller.abort(); +} + +{ + // Test close with pre-aborted signal. + const server = net.createServer(); + const signal = AbortSignal.abort(); + server.on('close', common.mustCall()); + server.listen({ port: 0, signal }); +} diff --git a/node/_tools/test/parallel/test-net-server-listen-options.js b/node/_tools/test/parallel/test-net-server-listen-options.js new file mode 100644 index 000000000000..5c42a52c4f6d --- /dev/null +++ b/node/_tools/test/parallel/test-net-server-listen-options.js @@ -0,0 +1,101 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +function close() { this.close(); } + +{ + // Test listen() + net.createServer().listen().on('listening', common.mustCall(close)); + // Test listen(cb) + net.createServer().listen(common.mustCall(close)); + // Test listen(port) + net.createServer().listen(0).on('listening', common.mustCall(close)); + // Test listen({port}) + net.createServer().listen({ port: 0 }) + .on('listening', common.mustCall(close)); +} + +// Test listen(port, cb) and listen({ port }, cb) combinations +const listenOnPort = [ + (port, cb) => net.createServer().listen({ port }, cb), + (port, cb) => net.createServer().listen(port, cb), +]; + +{ + const assertPort = () => { + return common.expectsError({ + code: 'ERR_SOCKET_BAD_PORT', + name: 'RangeError' + }); + }; + + for (const listen of listenOnPort) { + // Arbitrary unused ports + listen('0', common.mustCall(close)); + listen(0, common.mustCall(close)); + listen(undefined, common.mustCall(close)); + listen(null, common.mustCall(close)); + // Test invalid ports + assert.throws(() => listen(-1, common.mustNotCall()), assertPort()); + assert.throws(() => listen(NaN, common.mustNotCall()), assertPort()); + assert.throws(() => listen(123.456, common.mustNotCall()), assertPort()); + assert.throws(() => listen(65536, common.mustNotCall()), assertPort()); + assert.throws(() => listen(1 / 0, common.mustNotCall()), assertPort()); + assert.throws(() => listen(-1 / 0, common.mustNotCall()), assertPort()); + } + // In listen(options, cb), port takes precedence over path + assert.throws(() => { + net.createServer().listen({ port: -1, path: common.PIPE }, + common.mustNotCall()); + }, assertPort()); +} + +{ + function shouldFailToListen(options) { + const fn = () => { + net.createServer().listen(options, common.mustNotCall()); + }; + + if (typeof options === 'object' && + !(('port' in options) || ('path' in options))) { + assert.throws(fn, + { + code: 'ERR_INVALID_ARG_VALUE', + name: 'TypeError', + message: /^The argument 'options' must have the property "port" or "path"\. Received .+$/, + }); + } else { + assert.throws(fn, + { + code: 'ERR_INVALID_ARG_VALUE', + name: 'TypeError', + message: /^The argument 'options' is invalid\. Received .+$/, + }); + } + } + + shouldFailToListen(false, { port: false }); + shouldFailToListen({ port: false }); + shouldFailToListen(true); + shouldFailToListen({ port: true }); + // Invalid fd as listen(handle) + shouldFailToListen({ fd: -1 }); + // Invalid path in listen(options) + shouldFailToListen({ path: -1 }); + + // Neither port or path are specified in options + shouldFailToListen({}); + shouldFailToListen({ host: 'localhost' }); + shouldFailToListen({ host: 'localhost:3000' }); + shouldFailToListen({ host: { port: 3000 } }); + shouldFailToListen({ exclusive: true }); +} diff --git a/node/_tools/test/parallel/test-net-server-listen-path.js b/node/_tools/test/parallel/test-net-server-listen-path.js new file mode 100644 index 000000000000..559e9c7eb4a8 --- /dev/null +++ b/node/_tools/test/parallel/test-net-server-listen-path.js @@ -0,0 +1,100 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const net = require('net'); +const assert = require('assert'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +function closeServer() { + return common.mustCall(function() { + this.close(); + }); +} + +let counter = 0; + +// Avoid conflict with listen-handle +function randomPipePath() { + return `${common.PIPE}-listen-path-${counter++}`; +} + +// Test listen(path) +{ + const handlePath = randomPipePath(); + net.createServer() + .listen(handlePath) + .on('listening', closeServer()); +} + +// Test listen({path}) +{ + const handlePath = randomPipePath(); + net.createServer() + .listen({ path: handlePath }) + .on('listening', closeServer()); +} + +// Test listen(path, cb) +{ + const handlePath = randomPipePath(); + net.createServer() + .listen(handlePath, closeServer()); +} + +// Test listen(path, cb) +{ + const handlePath = randomPipePath(); + net.createServer() + .listen({ path: handlePath }, closeServer()); +} + +// Test pipe chmod +{ + const handlePath = randomPipePath(); + + const server = net.createServer() + .listen({ + path: handlePath, + readableAll: true, + writableAll: true + }, common.mustCall(() => { + if (process.platform !== 'win32') { + const mode = fs.statSync(handlePath).mode; + assert.notStrictEqual(mode & fs.constants.S_IROTH, 0); + assert.notStrictEqual(mode & fs.constants.S_IWOTH, 0); + } + server.close(); + })); +} + +// TODO(cmorten): seems Deno.listen() for Unix domains isn't throwing +// Deno.errors.AddrInUse errors as would expect...? +// Test should emit "error" events when listening fails. +// { +// const handlePath = randomPipePath(); +// const server1 = net.createServer().listen({ path: handlePath }, () => { +// // As the handlePath is in use, binding to the same address again should +// // make the server emit an 'EADDRINUSE' error. +// const server2 = net.createServer() +// .listen({ +// path: handlePath, +// writableAll: true, +// }, common.mustNotCall()); + +// server2.on('error', common.mustCall((err) => { +// server1.close(); +// assert.strictEqual(err.code, 'EADDRINUSE'); +// assert.match(err.message, /^listen EADDRINUSE: address already in use/); +// })); +// }); +// } diff --git a/node/_tools/test/parallel/test-net-server-listen-remove-callback.js b/node/_tools/test/parallel/test-net-server-listen-remove-callback.js new file mode 100644 index 000000000000..6d4ad7f4eab0 --- /dev/null +++ b/node/_tools/test/parallel/test-net-server-listen-remove-callback.js @@ -0,0 +1,51 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const net = require('net'); + +// Server should only fire listen callback once +const server = net.createServer(); + +server.on('close', function() { + const listeners = server.listeners('listening'); + console.log('Closed, listeners:', listeners.length); + assert.strictEqual(listeners.length, 0); +}); + +server.listen(0, function() { + server.close(); +}); + +server.once('close', function() { + server.listen(0, function() { + server.close(); + }); +}); diff --git a/node/_tools/test/parallel/test-net-server-max-connections.js b/node/_tools/test/parallel/test-net-server-max-connections.js new file mode 100644 index 000000000000..18513326dd2e --- /dev/null +++ b/node/_tools/test/parallel/test-net-server-max-connections.js @@ -0,0 +1,114 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const net = require('net'); + +// This test creates 20 connections to a server and sets the server's +// maxConnections property to 10. The first 10 connections make it through +// and the last 10 connections are rejected. + +const N = 20; +let closes = 0; +const waits = []; + +const server = net.createServer(common.mustCall(function(connection) { + connection.write('hello'); + waits.push(function() { connection.end(); }); +}, N / 2)); + +server.listen(0, function() { + makeConnection(0); +}); + +server.maxConnections = N / 2; + + +function makeConnection(index) { + const c = net.createConnection(server.address().port); + let gotData = false; + + c.on('connect', function() { + if (index + 1 < N) { + makeConnection(index + 1); + } + + c.on('close', function() { + console.error(`closed ${index}`); + closes++; + + if (closes < N / 2) { + assert.ok( + server.maxConnections <= index, + `${index} should not have been one of the first closed connections` + ); + } + + if (closes === N / 2) { + let cb; + console.error('calling wait callback.'); + while (cb = waits.shift()) { + cb(); + } + server.close(); + } + + if (index < server.maxConnections) { + assert.strictEqual(gotData, true, + `${index} didn't get data, but should have`); + } else { + assert.strictEqual(gotData, false, + `${index} got data, but shouldn't have`); + } + }); + }); + + c.on('end', function() { c.end(); }); + + c.on('data', function(b) { + gotData = true; + assert.ok(b.length > 0); + }); + + c.on('error', function(e) { + // Retry if SmartOS and ECONNREFUSED. See + // https://github.com/nodejs/node/issues/2663. + if (common.isSunOS && (e.code === 'ECONNREFUSED')) { + c.connect(server.address().port); + } + console.error(`error ${index}: ${e}`); + }); +} + + +process.on('exit', function() { + assert.strictEqual(closes, N); +}); diff --git a/node/_tools/test/parallel/test-net-server-options.js b/node/_tools/test/parallel/test-net-server-options.js new file mode 100644 index 000000000000..2b0e1a62f61e --- /dev/null +++ b/node/_tools/test/parallel/test-net-server-options.js @@ -0,0 +1,23 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const assert = require('assert'); +const net = require('net'); + +assert.throws(() => net.createServer('path'), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); + +assert.throws(() => net.createServer(0), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); diff --git a/node/_tools/test/parallel/test-net-server-pause-on-connect.js b/node/_tools/test/parallel/test-net-server-pause-on-connect.js new file mode 100644 index 000000000000..9b11f8a63d14 --- /dev/null +++ b/node/_tools/test/parallel/test-net-server-pause-on-connect.js @@ -0,0 +1,79 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); +const msg = 'test'; +let stopped = true; +let server1Sock; + + +const server1ConnHandler = (socket) => { + socket.on('data', function(data) { + if (stopped) { + assert.fail('data event should not have happened yet'); + } + + assert.strictEqual(data.toString(), msg); + socket.end(); + server1.close(); + }); + + server1Sock = socket; +}; + +const server1 = net.createServer({ pauseOnConnect: true }, server1ConnHandler); + +const server2ConnHandler = (socket) => { + socket.on('data', function(data) { + assert.strictEqual(data.toString(), msg); + socket.end(); + server2.close(); + + assert.strictEqual(server1Sock.bytesRead, 0); + server1Sock.resume(); + stopped = false; + }); +}; + +const server2 = net.createServer({ pauseOnConnect: false }, server2ConnHandler); + +server1.listen(0, function() { + const clientHandler = common.mustCall(function() { + server2.listen(0, function() { + net.createConnection({ port: this.address().port }).write(msg); + }); + }); + net.createConnection({ port: this.address().port }).write(msg, clientHandler); +}); + +process.on('exit', function() { + assert.strictEqual(stopped, false); +}); diff --git a/node/_tools/test/parallel/test-net-server-try-ports.js b/node/_tools/test/parallel/test-net-server-try-ports.js new file mode 100644 index 000000000000..69dbc78b8d2a --- /dev/null +++ b/node/_tools/test/parallel/test-net-server-try-ports.js @@ -0,0 +1,54 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +// This test binds to one port, then attempts to start a server on that +// port. It should be EADDRINUSE but be able to then bind to another port. +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +// TODO: support net.Server() without new + +const server1 = new net.Server(); + +const server2 = new net.Server(); + +server2.on('error', common.mustCall(function(e) { + assert.strictEqual(e.code, 'EADDRINUSE'); + + server2.listen(0, common.mustCall(function() { + server1.close(); + server2.close(); + })); +})); + +server1.listen(0, common.mustCall(function() { + // This should make server2 emit EADDRINUSE + server2.listen(this.address().port); +})); diff --git a/node/_tools/test/parallel/test-net-server-unref.js b/node/_tools/test/parallel/test-net-server-unref.js new file mode 100644 index 000000000000..b5e12ea795e2 --- /dev/null +++ b/node/_tools/test/parallel/test-net-server-unref.js @@ -0,0 +1,37 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const net = require('net'); + +const s = net.createServer(); +s.listen(0); +s.unref(); + +setTimeout(common.mustNotCall(), 1000).unref(); diff --git a/node/_tools/test/parallel/test-net-socket-close-after-end.js b/node/_tools/test/parallel/test-net-socket-close-after-end.js new file mode 100644 index 000000000000..c1cea0324fd9 --- /dev/null +++ b/node/_tools/test/parallel/test-net-socket-close-after-end.js @@ -0,0 +1,38 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); + +const assert = require('assert'); +const net = require('net'); + +const server = net.createServer(); + +server.on('connection', (socket) => { + let endEmitted = false; + + socket.once('readable', () => { + setTimeout(() => { + socket.read(); + }, common.platformTimeout(100)); + }); + socket.on('end', () => { + endEmitted = true; + }); + socket.on('close', () => { + assert(endEmitted); + server.close(); + }); + socket.end('foo'); +}); + +server.listen(common.mustCall(() => { + const socket = net.createConnection(server.address().port, () => { + socket.end('foo'); + }); +})); diff --git a/node/_tools/test/parallel/test-net-socket-connect-without-cb.js b/node/_tools/test/parallel/test-net-socket-connect-without-cb.js new file mode 100644 index 000000000000..12699c1f433b --- /dev/null +++ b/node/_tools/test/parallel/test-net-socket-connect-without-cb.js @@ -0,0 +1,27 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'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('connect', common.mustCall(function() { + client.end(); + })); + + client.connect(server.address()); +})); diff --git a/node/_tools/test/parallel/test-net-socket-connecting.js b/node/_tools/test/parallel/test-net-socket-connecting.js new file mode 100644 index 000000000000..739fac8cb2cc --- /dev/null +++ b/node/_tools/test/parallel/test-net-socket-connecting.js @@ -0,0 +1,28 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); +const assert = require('assert'); +const net = require('net'); + +const server = net.createServer((conn) => { + conn.end(); + server.close(); +}).listen(0, () => { + const client = net.connect(server.address().port, () => { + assert.strictEqual(client.connecting, false); + + // Legacy getter + assert.strictEqual(client._connecting, false); + client.end(); + }); + assert.strictEqual(client.connecting, true); + + // Legacy getter + assert.strictEqual(client._connecting, true); +}); diff --git a/node/_tools/test/parallel/test-net-socket-destroy-send.js b/node/_tools/test/parallel/test-net-socket-destroy-send.js new file mode 100644 index 000000000000..3373287a1e68 --- /dev/null +++ b/node/_tools/test/parallel/test-net-socket-destroy-send.js @@ -0,0 +1,31 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const net = require('net'); +const assert = require('assert'); + +const server = net.createServer(); +server.listen(0, common.mustCall(function() { + const port = server.address().port; + const conn = net.createConnection(port); + + conn.on('connect', common.mustCall(function() { + // Test destroy returns this, even on multiple calls when it short-circuits. + assert.strictEqual(conn, conn.destroy().destroy()); + conn.on('error', common.mustNotCall()); + + conn.write(Buffer.from('kaboom'), common.expectsError({ + code: 'ERR_STREAM_DESTROYED', + message: 'Cannot call write after a stream was destroyed', + name: 'Error' + })); + server.close(); + })); +})); diff --git a/node/_tools/test/parallel/test-net-socket-destroy-twice.js b/node/_tools/test/parallel/test-net-socket-destroy-twice.js new file mode 100644 index 000000000000..296afb26164f --- /dev/null +++ b/node/_tools/test/parallel/test-net-socket-destroy-twice.js @@ -0,0 +1,43 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const net = require('net'); + +const server = net.createServer(); +server.listen(0); +const port = server.address().port; +const conn = net.createConnection(port); + +conn.on('error', common.mustCall(() => { + conn.destroy(); +})); + +conn.on('close', common.mustCall()); +server.close(); diff --git a/node/_tools/test/parallel/test-net-socket-end-before-connect.js b/node/_tools/test/parallel/test-net-socket-end-before-connect.js new file mode 100644 index 000000000000..f10e18ad2641 --- /dev/null +++ b/node/_tools/test/parallel/test-net-socket-end-before-connect.js @@ -0,0 +1,20 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); + +const net = require('net'); + +const server = net.createServer(); + +server.listen(common.mustCall(() => { + const socket = net.createConnection(server.address().port); + socket.on('close', common.mustCall(() => server.close())); + socket.end(); +})); diff --git a/node/_tools/test/parallel/test-net-socket-end-callback.js b/node/_tools/test/parallel/test-net-socket-end-callback.js new file mode 100644 index 000000000000..9778695c54b2 --- /dev/null +++ b/node/_tools/test/parallel/test-net-socket-end-callback.js @@ -0,0 +1,29 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const net = require('net'); + +const server = net.createServer((socket) => { + socket.resume(); +}).unref(); + +server.listen(common.mustCall(() => { + const connect = (...args) => { + const socket = net.createConnection(server.address().port, () => { + socket.end(...args); + }); + }; + + const cb = common.mustCall(() => {}, 3); + + connect(cb); + connect('foo', cb); + connect('foo', 'utf8', cb); +})); diff --git a/node/_tools/test/parallel/test-net-socket-no-halfopen-enforcer.js b/node/_tools/test/parallel/test-net-socket-no-halfopen-enforcer.js new file mode 100644 index 000000000000..f8f4eb7c0b25 --- /dev/null +++ b/node/_tools/test/parallel/test-net-socket-no-halfopen-enforcer.js @@ -0,0 +1,18 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +require('../common'); + +// This test ensures that `net.Socket` does not inherit the no-half-open +// enforcer from `stream.Duplex`. + +const { Socket } = require('net'); +const { strictEqual } = require('assert'); + +const socket = new Socket({ allowHalfOpen: false }); +strictEqual(socket.listenerCount('end'), 1); diff --git a/node/_tools/test/parallel/test-net-socket-ready-without-cb.js b/node/_tools/test/parallel/test-net-socket-ready-without-cb.js new file mode 100644 index 000000000000..fa612e891146 --- /dev/null +++ b/node/_tools/test/parallel/test-net-socket-ready-without-cb.js @@ -0,0 +1,27 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'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()); +})); diff --git a/node/_tools/test/parallel/test-net-socket-timeout.js b/node/_tools/test/parallel/test-net-socket-timeout.js new file mode 100644 index 000000000000..845d08f4cc79 --- /dev/null +++ b/node/_tools/test/parallel/test-net-socket-timeout.js @@ -0,0 +1,90 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const net = require('net'); +const assert = require('assert'); +const { inspect } = require('util'); + +// Verify that invalid delays throw +const s = new net.Socket(); +const nonNumericDelays = [ + '100', true, false, undefined, null, '', {}, () => {}, [], +]; +const badRangeDelays = [-0.001, -1, -Infinity, Infinity, NaN]; +const validDelays = [0, 0.001, 1, 1e6]; +const invalidCallbacks = [ + 1, '100', true, false, null, {}, [], Symbol('test'), +]; + + +for (let i = 0; i < nonNumericDelays.length; i++) { + assert.throws(() => { + s.setTimeout(nonNumericDelays[i], () => {}); + }, { code: 'ERR_INVALID_ARG_TYPE' }, nonNumericDelays[i]); +} + +for (let i = 0; i < badRangeDelays.length; i++) { + assert.throws(() => { + s.setTimeout(badRangeDelays[i], () => {}); + }, { code: 'ERR_OUT_OF_RANGE' }, badRangeDelays[i]); +} + +for (let i = 0; i < validDelays.length; i++) { + s.setTimeout(validDelays[i], () => {}); +} + +for (let i = 0; i < invalidCallbacks.length; i++) { + [0, 1].forEach((mesc) => + assert.throws( + () => s.setTimeout(mesc, invalidCallbacks[i]), + { + code: 'ERR_INVALID_CALLBACK', + name: 'TypeError', + message: 'Callback must be a function. ' + + `Received ${inspect(invalidCallbacks[i])}` + } + ) + ); +} + +// TODO: support net.Server() without new + +const server = new net.Server(); +server.listen(0, common.mustCall(() => { + const socket = net.createConnection(server.address().port); + assert.strictEqual( + socket.setTimeout(1, common.mustCall(() => { + socket.destroy(); + assert.strictEqual(socket.setTimeout(1, common.mustNotCall()), socket); + server.close(); + })), + socket + ); +})); diff --git a/node/_tools/test/parallel/test-net-socket-write-after-close.js b/node/_tools/test/parallel/test-net-socket-write-after-close.js new file mode 100644 index 000000000000..97e8b39f5c6f --- /dev/null +++ b/node/_tools/test/parallel/test-net-socket-write-after-close.js @@ -0,0 +1,49 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +{ + const server = net.createServer(); + + server.listen(common.mustCall(() => { + const port = server.address().port; + const client = net.connect({ port }, common.mustCall(() => { + client.on('error', common.mustCall((err) => { + server.close(); + assert.strictEqual(err.constructor, Error); + assert.strictEqual(err.message, 'write EBADF'); + })); + client._handle.close(); + client.write('foo'); + })); + })); +} + +{ + const server = net.createServer(); + + server.listen(common.mustCall(() => { + const port = server.address().port; + const client = net.connect({ port }, common.mustCall(() => { + client.on('error', common.expectsError({ + code: 'ERR_SOCKET_CLOSED', + message: 'Socket is closed', + name: 'Error' + })); + + server.close(); + + client._handle.close(); + client._handle = null; + client.write('foo'); + })); + })); +} diff --git a/node/_tools/test/parallel/test-net-socket-write-error.js b/node/_tools/test/parallel/test-net-socket-write-error.js new file mode 100644 index 000000000000..832fad51a601 --- /dev/null +++ b/node/_tools/test/parallel/test-net-socket-write-error.js @@ -0,0 +1,29 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const net = require('net'); +const assert = require('assert'); + +const server = net.createServer().listen(0, connectToServer); + +function connectToServer() { + const client = net.createConnection(this.address().port, () => { + client.on('error', common.mustNotCall()); + assert.throws(() => { + client.write(1337); + }, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); + + client.destroy(); + }) + .on('close', () => server.close()); +} diff --git a/node/_tools/test/parallel/test-net-sync-cork.js b/node/_tools/test/parallel/test-net-sync-cork.js new file mode 100644 index 000000000000..74872eaba9aa --- /dev/null +++ b/node/_tools/test/parallel/test-net-sync-cork.js @@ -0,0 +1,40 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const server = net.createServer(handle); + +const N = 100; +const buf = Buffer.alloc(2, 'a'); + +server.listen(0, function() { + const conn = net.connect(this.address().port); + + conn.on('connect', () => { + let res = true; + let i = 0; + for (; i < N && res; i++) { + conn.cork(); + conn.write(buf); + res = conn.write(buf); + conn.uncork(); + } + assert.strictEqual(i, N); + conn.end(); + }); +}); + +function handle(socket) { + socket.resume(); + socket.on('error', common.mustNotCall()) + .on('close', common.mustCall(() => server.close())); +} diff --git a/node/_tools/test/parallel/test-net-timeout-no-handle.js b/node/_tools/test/parallel/test-net-timeout-no-handle.js new file mode 100644 index 000000000000..f2a648f1bb75 --- /dev/null +++ b/node/_tools/test/parallel/test-net-timeout-no-handle.js @@ -0,0 +1,24 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; + +const common = require('../common'); +const net = require('net'); +const assert = require('assert'); + +const socket = new net.Socket(); +socket.setTimeout(common.platformTimeout(50)); + +socket.on('timeout', common.mustCall(() => { + assert.strictEqual(socket._handle, null); +})); + +socket.on('connect', common.mustNotCall()); + +// Since the timeout is unrefed, the code will exit without this +setTimeout(() => {}, common.platformTimeout(200)); diff --git a/node/_tools/test/parallel/test-net-writable.js b/node/_tools/test/parallel/test-net-writable.js new file mode 100644 index 000000000000..4cfbccbcb974 --- /dev/null +++ b/node/_tools/test/parallel/test-net-writable.js @@ -0,0 +1,22 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const server = net.createServer(common.mustCall(function(s) { + server.close(); + s.end(); +})).listen(0, 'localhost', common.mustCall(function() { + const socket = net.connect(this.address().port, 'localhost'); + socket.on('end', common.mustCall(() => { + assert.strictEqual(socket.writable, true); + socket.write('hello world'); + })); +})); diff --git a/node/_tools/test/parallel/test-net-write-after-end-nt.js b/node/_tools/test/parallel/test-net-write-after-end-nt.js new file mode 100644 index 000000000000..032a0c39c900 --- /dev/null +++ b/node/_tools/test/parallel/test-net-write-after-end-nt.js @@ -0,0 +1,39 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); + +const assert = require('assert'); +const net = require('net'); + +const { expectsError, mustCall } = common; + +// This test ensures those errors caused by calling `net.Socket.write()` +// after sockets ending will be emitted in the next tick. +const server = net.createServer(mustCall((socket) => { + socket.end(); +})).listen(() => { + const client = net.connect(server.address().port, () => { + let hasError = false; + client.on('error', mustCall((err) => { + hasError = true; + server.close(); + })); + client.on('end', mustCall(() => { + const ret = client.write('hello', expectsError({ + code: 'EPIPE', + message: 'This socket has been ended by the other party', + name: 'Error' + })); + + assert.strictEqual(ret, false); + assert(!hasError, 'The error should be emitted in the next tick.'); + })); + client.end(); + }); +}); diff --git a/node/_tools/test/parallel/test-net-write-arguments.js b/node/_tools/test/parallel/test-net-write-arguments.js new file mode 100644 index 000000000000..d6beb72ee162 --- /dev/null +++ b/node/_tools/test/parallel/test-net-write-arguments.js @@ -0,0 +1,46 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +const common = require('../common'); +const net = require('net'); +const assert = require('assert'); +const socket = new net.Stream({ highWaterMark: 0 }); + +// Make sure that anything besides a buffer or a string throws. +socket.on('error', common.mustNotCall()); +assert.throws(() => { + socket.write(null); +}, { + code: 'ERR_STREAM_NULL_VALUES', + name: 'TypeError', + message: 'May not write null values to stream' +}); + +[ + true, + false, + undefined, + 1, + 1.0, + +Infinity, + -Infinity, + [], + {}, +].forEach((value) => { + const socket = new net.Stream({ highWaterMark: 0 }); + // We need to check the callback since 'error' will only + // be emitted once per instance. + assert.throws(() => { + socket.write(value); + }, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "chunk" argument must be of type string or an instance of ' + + `Buffer or Uint8Array.${common.invalidArgTypeHelper(value)}` + }); +}); diff --git a/node/_tools/test/parallel/test-net-write-fully-async-buffer.js b/node/_tools/test/parallel/test-net-write-fully-async-buffer.js new file mode 100644 index 000000000000..679b4b254e6b --- /dev/null +++ b/node/_tools/test/parallel/test-net-write-fully-async-buffer.js @@ -0,0 +1,41 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +// Flags: --expose-gc + +// Note: This is a variant of test-net-write-fully-async-hex-string.js. +// This always worked, but it seemed appropriate to add a test that checks the +// behavior for Buffers, too. +const common = require('../common'); +const net = require('net'); + +const data = Buffer.alloc(1000000); + +const server = net.createServer(common.mustCall(function(conn) { + conn.resume(); +})).listen(0, common.mustCall(function() { + const conn = net.createConnection(this.address().port, common.mustCall(() => { + let count = 0; + + function writeLoop() { + if (count++ === 200) { + conn.destroy(); + server.close(); + return; + } + + while (conn.write(Buffer.from(data))); + global.gc(true); + // The buffer allocated above should still be alive. + } + + conn.on('drain', writeLoop); + + writeLoop(); + })); +})); diff --git a/node/_tools/test/parallel/test-net-write-fully-async-hex-string.js b/node/_tools/test/parallel/test-net-write-fully-async-hex-string.js new file mode 100644 index 000000000000..3e83b36c5c3f --- /dev/null +++ b/node/_tools/test/parallel/test-net-write-fully-async-hex-string.js @@ -0,0 +1,39 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +'use strict'; +// Flags: --expose-gc + +// Regression test for https://github.com/nodejs/node/issues/8251. +const common = require('../common'); +const net = require('net'); + +const data = Buffer.alloc(1000000).toString('hex'); + +const server = net.createServer(common.mustCall(function(conn) { + conn.resume(); +})).listen(0, common.mustCall(function() { + const conn = net.createConnection(this.address().port, common.mustCall(() => { + let count = 0; + + function writeLoop() { + if (count++ === 20) { + conn.destroy(); + server.close(); + return; + } + + while (conn.write(data, 'hex')); + global.gc(true); + // The buffer allocated inside the .write() call should still be alive. + } + + conn.on('drain', writeLoop); + + writeLoop(); + })); +})); diff --git a/node/_tools/test/parallel/test-net-write-slow.js b/node/_tools/test/parallel/test-net-write-slow.js new file mode 100644 index 000000000000..dabcc3863bd6 --- /dev/null +++ b/node/_tools/test/parallel/test-net-write-slow.js @@ -0,0 +1,70 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const SIZE = 2E5; +const N = 10; +let flushed = 0; +let received = 0; +const buf = Buffer.alloc(SIZE, 'a'); + +const server = net.createServer(function(socket) { + socket.setNoDelay(); + socket.setTimeout(9999); + socket.on('timeout', function() { + assert.fail(`flushed: ${flushed}, received: ${received}/${SIZE * N}`); + }); + + for (let i = 0; i < N; ++i) { + socket.write(buf, function() { + ++flushed; + if (flushed === N) { + socket.setTimeout(0); + } + }); + } + socket.end(); + +}).listen(0, common.mustCall(function() { + const conn = net.connect(this.address().port); + conn.on('data', function(buf) { + received += buf.length; + conn.pause(); + setTimeout(function() { + conn.resume(); + }, 20); + }); + conn.on('end', common.mustCall(function() { + server.close(); + assert.strictEqual(received, SIZE * N); + })); +})); diff --git a/node/_tools/test/pummel/test-net-bytes-per-incoming-chunk-overhead.js b/node/_tools/test/pummel/test-net-bytes-per-incoming-chunk-overhead.js new file mode 100644 index 000000000000..950fc74ec014 --- /dev/null +++ b/node/_tools/test/pummel/test-net-bytes-per-incoming-chunk-overhead.js @@ -0,0 +1,58 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Flags: --expose-gc +'use strict'; + +const common = require('../common'); + +if (process.config.variables.asan) { + common.skip('ASAN messes with memory measurements'); +} + +if (process.config.variables.arm_version === '7') { + common.skip('Too slow for armv7 bots'); +} + +const assert = require('assert'); +const net = require('net'); + +// Tests that, when receiving small chunks, we do not keep the full length +// of the original allocation for the libuv read call in memory. + +let client; +let baseRSS; +const receivedChunks = []; +const N = 250000; + +const server = net.createServer(common.mustCall((socket) => { + baseRSS = process.memoryUsage.rss(); + + socket.setNoDelay(true); + socket.on('data', (chunk) => { + receivedChunks.push(chunk); + if (receivedChunks.length < N) { + client.write('a'); + } else { + client.end(); + server.close(); + } + }); +})).listen(0, common.mustCall(() => { + client = net.connect(server.address().port); + client.setNoDelay(true); + client.write('hello!'); +})); + +process.on('exit', () => { + // TODO: support global.gc() compat + // global.gc(); + const bytesPerChunk = + (process.memoryUsage.rss() - baseRSS) / receivedChunks.length; + // We should always have less than one page (usually ~ 4 kB) per chunk. + assert(bytesPerChunk < 650, `measured ${bytesPerChunk} bytes per chunk`); +}); diff --git a/node/_tools/test/pummel/test-net-pingpong-delay.js b/node/_tools/test/pummel/test-net-pingpong-delay.js new file mode 100644 index 000000000000..9be87ad202a1 --- /dev/null +++ b/node/_tools/test/pummel/test-net-pingpong-delay.js @@ -0,0 +1,114 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +function pingPongTest(host, on_complete) { + const N = 100; + const DELAY = 1; + let count = 0; + let client_ended = false; + + const server = net.createServer({ allowHalfOpen: true }, function(socket) { + socket.setEncoding('utf8'); + + socket.on('data', function(data) { + console.log(data); + assert.strictEqual(data, 'PING'); + assert.strictEqual(socket.readyState, 'open'); + assert.strictEqual(count <= N, true); + setTimeout(function() { + assert.strictEqual(socket.readyState, 'open'); + socket.write('PONG'); + }, DELAY); + }); + + socket.on('timeout', function() { + console.error('server-side timeout!!'); + assert.strictEqual(false, true); + }); + + socket.on('end', function() { + console.log('server-side socket EOF'); + assert.strictEqual(socket.readyState, 'writeOnly'); + socket.end(); + }); + + socket.on('close', function(had_error) { + console.log('server-side socket.end'); + assert.strictEqual(had_error, false); + assert.strictEqual(socket.readyState, 'closed'); + socket.server.close(); + }); + }); + + server.listen(0, host, common.mustCall(function() { + const client = net.createConnection(server.address().port, host); + + client.setEncoding('utf8'); + + client.on('connect', function() { + assert.strictEqual(client.readyState, 'open'); + client.write('PING'); + }); + + client.on('data', function(data) { + console.log(data); + assert.strictEqual(data, 'PONG'); + assert.strictEqual(client.readyState, 'open'); + + setTimeout(function() { + assert.strictEqual(client.readyState, 'open'); + if (count++ < N) { + client.write('PING'); + } else { + console.log('closing client'); + client.end(); + client_ended = true; + } + }, DELAY); + }); + + client.on('timeout', function() { + console.error('client-side timeout!!'); + assert.strictEqual(false, true); + }); + + client.on('close', common.mustCall(function() { + console.log('client.end'); + assert.strictEqual(count, N + 1); + assert.ok(client_ended); + if (on_complete) on_complete(); + })); + })); +} + +pingPongTest(); diff --git a/node/_tools/test/pummel/test-net-write-callbacks.js b/node/_tools/test/pummel/test-net-write-callbacks.js new file mode 100644 index 000000000000..7f6528107705 --- /dev/null +++ b/node/_tools/test/pummel/test-net-write-callbacks.js @@ -0,0 +1,80 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const net = require('net'); +const assert = require('assert'); + +let cbcount = 0; +const N = 500000; + +// TODO: support net.Server() without new + +const server = new net.Server(function(socket) { + socket.on('data', function(d) { + console.error(`got ${d.length} bytes`); + }); + + socket.on('end', function() { + console.error('end'); + socket.destroy(); + server.close(); + }); +}); + +let lastCalled = -1; +function makeCallback(c) { + let called = false; + return function() { + if (called) + throw new Error(`called callback #${c} more than once`); + called = true; + if (c < lastCalled) { + throw new Error( + `callbacks out of order. last=${lastCalled} current=${c}`); + } + lastCalled = c; + cbcount++; + }; +} + +server.listen(0, function() { + const client = net.createConnection(server.address().port); + + client.on('connect', function() { + for (let i = 0; i < N; i++) { + client.write('hello world', makeCallback(i)); + } + client.end(); + }); +}); + +process.on('exit', function() { + assert.strictEqual(cbcount, N); +}); diff --git a/node/internal/errors.ts b/node/internal/errors.ts index 88d58627e179..adca01bf10ae 100644 --- a/node/internal/errors.ts +++ b/node/internal/errors.ts @@ -2551,7 +2551,24 @@ codes.ERR_BUFFER_OUT_OF_BOUNDS = ERR_BUFFER_OUT_OF_BOUNDS; codes.ERR_UNKNOWN_ENCODING = ERR_UNKNOWN_ENCODING; // TODO(kt3k): assign all error classes here. -export { codes, hideStackFrames }; +/** + * This creates a generic Node.js error. + * + * @param {string} message The error message. + * @param {object} errorProperties Object with additional properties to be added to the error. + * @returns {Error} + */ +const genericNodeError = hideStackFrames( + function genericNodeError(message, errorProperties) { + // eslint-disable-next-line no-restricted-syntax + const err = new Error(message); + Object.assign(err, errorProperties); + + return err; + }, +); + +export { codes, genericNodeError, hideStackFrames }; export default { AbortError, diff --git a/node/internal_binding/_listen.ts b/node/internal_binding/_listen.ts new file mode 100644 index 000000000000..2aa93585e02c --- /dev/null +++ b/node/internal_binding/_listen.ts @@ -0,0 +1,15 @@ +/** + * @param n Number to act on. + * @return The number rounded up to the nearest power of 2. + */ +export function ceilPowOf2(n: number) { + const roundPowOf2 = 1 << (31 - Math.clz32(n)); + + return roundPowOf2 < n ? roundPowOf2 * 2 : roundPowOf2; +} + +/** Initial backoff delay of 5ms following a temporary accept failure. */ +export const INITIAL_ACCEPT_BACKOFF_DELAY = 5; + +/** Max backoff delay of 1s following a temporary accept failure. */ +export const MAX_ACCEPT_BACKOFF_DELAY = 1000; diff --git a/node/internal_binding/cares_wrap.ts b/node/internal_binding/cares_wrap.ts index 0de0c43dff07..34553af2fd0b 100644 --- a/node/internal_binding/cares_wrap.ts +++ b/node/internal_binding/cares_wrap.ts @@ -28,6 +28,7 @@ import type { ErrnoException } from "../internal/errors.ts"; import { isIPv4 } from "../internal/net.ts"; import { codeMap } from "./uv.ts"; import { AsyncWrap, providerType } from "./async_wrap.ts"; +import { isWindows } from "../../_util/os.ts"; interface LookupAddress { address: string; @@ -69,7 +70,7 @@ export function getaddrinfo( _hints: number, verbatim: boolean, ): number { - const addresses: string[] = []; + let addresses: string[] = []; // TODO(cmorten): use hints // REF: https://nodejs.org/api/dns.html#dns_supported_getaddrinfo_flags @@ -94,6 +95,8 @@ export function getaddrinfo( const error = addresses.length ? 0 : codeMap.get("EAI_NODATA")!; + // TODO(cmorten): needs work + // REF: https://github.com/nodejs/node/blob/master/src/cares_wrap.cc#L1444 if (!verbatim) { addresses.sort((a: string, b: string): number => { if (isIPv4(a)) { @@ -106,6 +109,13 @@ export function getaddrinfo( }); } + // TODO: Forces IPv4 as a workaround for Deno not + // aligning with Node on implicit binding on Windows + // REF: https://github.com/denoland/deno/issues/10762 + if (isWindows && hostname === "localhost") { + addresses = addresses.filter((address) => isIPv4(address)); + } + req.oncomplete(error, addresses); })(); diff --git a/node/internal_binding/handle_wrap.ts b/node/internal_binding/handle_wrap.ts index 2bfb08053b61..89313fe335ff 100644 --- a/node/internal_binding/handle_wrap.ts +++ b/node/internal_binding/handle_wrap.ts @@ -25,7 +25,6 @@ // - https://github.com/nodejs/node/blob/master/src/handle_wrap.h import { notImplemented } from "../_utils.ts"; -import { nextTick } from "../_next_tick.ts"; import { AsyncWrap, providerType } from "./async_wrap.ts"; export class HandleWrap extends AsyncWrap { @@ -33,9 +32,9 @@ export class HandleWrap extends AsyncWrap { super(provider); } - async close(cb: () => void = () => {}): Promise { - await this._onClose(); - nextTick(cb); + close(cb: () => void = () => {}): void { + this._onClose(); + cb(); } ref() { @@ -47,5 +46,5 @@ export class HandleWrap extends AsyncWrap { } // deno-lint-ignore no-explicit-any - async _onClose(): Promise {} + _onClose(): any {} } diff --git a/node/internal_binding/pipe_wrap.ts b/node/internal_binding/pipe_wrap.ts index bb526590cffb..1b1cf91ae97b 100644 --- a/node/internal_binding/pipe_wrap.ts +++ b/node/internal_binding/pipe_wrap.ts @@ -28,6 +28,18 @@ import { notImplemented } from "../_utils.ts"; import { unreachable } from "../../testing/asserts.ts"; import { ConnectionWrap } from "./connection_wrap.ts"; import { AsyncWrap, providerType } from "./async_wrap.ts"; +import { LibuvStreamWrap } from "./stream_wrap.ts"; +import { codeMap } from "./uv.ts"; +import { delay } from "../../async/mod.ts"; +import { kStreamBaseField } from "./stream_wrap.ts"; +import { + ceilPowOf2, + INITIAL_ACCEPT_BACKOFF_DELAY, + MAX_ACCEPT_BACKOFF_DELAY, +} from "./_listen.ts"; +import { isWindows } from "../../_util/os.ts"; +import { fs } from "./constants.ts"; +import * as DenoUnstable from "../../_deno_unstable.ts"; export enum socketType { SOCKET, @@ -39,7 +51,19 @@ export class Pipe extends ConnectionWrap { override reading = false; ipc: boolean; - constructor(type: number) { + // REF: https://github.com/nodejs/node/blob/master/deps/uv/src/win/pipe.c#L48 + #pendingInstances = 4; + + #address?: string; + + #backlog?: number; + #listener!: Deno.Listener; + #connections = 0; + + #closed = false; + #acceptBackoffDelay?: number; + + constructor(type: number, conn?: Deno.UnixConn) { let provider: providerType; let ipc: boolean; @@ -67,44 +91,281 @@ export class Pipe extends ConnectionWrap { } } - super(provider); + super(provider, conn); + this.ipc = ipc; + + if (conn && provider === providerType.PIPEWRAP) { + const localAddr = conn.localAddr as Deno.UnixAddr; + this.#address = localAddr.path; + } } - bind() { - notImplemented("Pipe.prototype.bind"); + open(_fd: number): number { + // REF: https://github.com/denoland/deno/issues/6529 + notImplemented("Pipe.prototype.open"); } - listen() { - notImplemented("Pipe.prototype.listen"); + /** + * Bind to a Unix domain or Windows named pipe. + * @param name Unix domain or Windows named pipe the server should listen to. + * @return An error status code. + */ + bind(name: string) { + // Deno doesn't currently separate bind from connect. For now we noop under + // the assumption we will connect shortly. + // REF: https://doc.deno.land/deno/unstable/~/Deno.connect + + this.#address = name; + + return 0; } - connect( - _req: PipeConnectWrap, - _address: string, - _afterConnect: ( - status: number, - handle: Pipe, - req: PipeConnectWrap, - readable: boolean, - writable: boolean, - ) => void, - ) { - notImplemented("Pipe.prototype.connect"); + /** + * Connect to a Unix domain or Windows named pipe. + * @param req A PipeConnectWrap instance. + * @param address Unix domain or Windows named pipe the server should connect to. + * @return An error status code. + */ + connect(req: PipeConnectWrap, address: string) { + if (isWindows) { + // REF: https://github.com/denoland/deno/issues/10244 + notImplemented("Pipe.prototype.connect - Windows"); + } + + const connectOptions: DenoUnstable.UnixConnectOptions = { + path: address, + transport: "unix", + }; + + DenoUnstable.connect(connectOptions).then( + (conn: Deno.UnixConn) => { + const localAddr = conn.localAddr as Deno.UnixAddr; + + this.#address = req.address = localAddr.path; + this[kStreamBaseField] = conn; + + try { + this.afterConnect(req, 0); + } catch { + // swallow callback errors. + } + }, + (e) => { + // TODO(cmorten): correct mapping of connection error to status code. + let code: number; + + if (e instanceof Deno.errors.NotFound) { + code = codeMap.get("ENOENT")!; + } else if (e instanceof Deno.errors.PermissionDenied) { + code = codeMap.get("EACCES")!; + } else { + code = codeMap.get("ECONNREFUSED")!; + } + + try { + this.afterConnect(req, code); + } catch { + // swallow callback errors. + } + }, + ); + + return 0; } - open(_fd: number): number { - // REF: https://github.com/denoland/deno/issues/6529 - notImplemented("Pipe.prototype.open"); + /** + * Listen for new connections. + * @param backlog The maximum length of the queue of pending connections. + * @return An error status code. + */ + listen(backlog: number): number { + if (isWindows) { + // REF: https://github.com/denoland/deno/issues/10244 + notImplemented("Pipe.prototype.listen - Windows"); + } + + this.#backlog = isWindows + ? this.#pendingInstances + : ceilPowOf2(backlog + 1); + + const listenOptions = { + path: this.#address!, + transport: "unix" as const, + }; + + let listener; + + try { + listener = DenoUnstable.listen(listenOptions); + } catch (e) { + if (e instanceof Deno.errors.AddrInUse) { + return codeMap.get("EADDRINUSE")!; + } else if (e instanceof Deno.errors.AddrNotAvailable) { + return codeMap.get("EADDRNOTAVAIL")!; + } + + // TODO(cmorten): map errors to appropriate error codes. + return codeMap.get("UNKNOWN")!; + } + + const address = listener.addr as Deno.UnixAddr; + this.#address = address.path; + + this.#listener = listener; + this.#accept(); + + return 0; + } + + override ref() { + if (this.#listener) { + DenoUnstable.ListenerRef(this.#listener); + } + } + + override unref() { + if (this.#listener) { + DenoUnstable.ListenerUnref(this.#listener); + } + } + + /** + * Set the number of pending pipe instance handles when the pipe server is + * waiting for connections. This setting applies to Windows only. + * @param instances Number of pending pipe instances. + */ + setPendingInstances(instances: number) { + this.#pendingInstances = instances; } - // Windows only - setPendingInstances(_instances: number) { - notImplemented("Pipe.prototype.setPendingInstances"); + /** + * Alters pipe permissions, allowing it to be accessed from processes run by + * different users. Makes the pipe writable or readable by all users. Mode + * can be `UV_WRITABLE`, `UV_READABLE` or `UV_WRITABLE | UV_READABLE`. This + * function is blocking. + * @param mode Pipe permissions mode. + * @return An error status code. + */ + fchmod(mode: number) { + if ( + mode != constants.UV_READABLE && + mode != constants.UV_WRITABLE && + mode != (constants.UV_WRITABLE | constants.UV_READABLE) + ) { + return codeMap.get("EINVAL"); + } + + let desired_mode = 0; + + if (mode & constants.UV_READABLE) { + desired_mode |= fs.S_IRUSR | fs.S_IRGRP | fs.S_IROTH; + } + if (mode & constants.UV_WRITABLE) { + desired_mode |= fs.S_IWUSR | fs.S_IWGRP | fs.S_IWOTH; + } + + // TODO(cmorten): this will incorrectly throw on Windows + // REF: https://github.com/denoland/deno/issues/4357 + try { + Deno.chmodSync(this.#address!, desired_mode); + } catch { + // TODO(cmorten): map errors to appropriate error codes. + return codeMap.get("UNKNOWN")!; + } + + return 0; } - fchmod() { - notImplemented("Pipe.prototype.fchmod"); + /** Handle backoff delays following an unsuccessful accept. */ + async #acceptBackoff(): Promise { + // Backoff after transient errors to allow time for the system to + // recover, and avoid blocking up the event loop with a continuously + // running loop. + if (!this.#acceptBackoffDelay) { + this.#acceptBackoffDelay = INITIAL_ACCEPT_BACKOFF_DELAY; + } else { + this.#acceptBackoffDelay *= 2; + } + + if (this.#acceptBackoffDelay >= MAX_ACCEPT_BACKOFF_DELAY) { + this.#acceptBackoffDelay = MAX_ACCEPT_BACKOFF_DELAY; + } + + await delay(this.#acceptBackoffDelay); + + this.#accept(); + } + + /** Accept new connections. */ + async #accept(): Promise { + if (this.#closed) { + return; + } + + if (this.#connections > this.#backlog!) { + this.#acceptBackoff(); + + return; + } + + let connection: Deno.Conn; + + try { + connection = await this.#listener.accept(); + } catch (e) { + if (e instanceof Deno.errors.BadResource && this.#closed) { + // Listener and server has closed. + return; + } + + try { + // TODO(cmorten): map errors to appropriate error codes. + this.onconnection!(codeMap.get("UNKNOWN")!, undefined); + } catch { + // swallow callback errors. + } + + this.#acceptBackoff(); + + return; + } + + // Reset the backoff delay upon successful accept. + this.#acceptBackoffDelay = undefined; + + const connectionHandle = new Pipe(socketType.SOCKET, connection); + this.#connections++; + + try { + this.onconnection!(0, connectionHandle); + } catch { + // swallow callback errors. + } + + return this.#accept(); + } + + /** Handle server closure. */ + override _onClose(): number { + this.#closed = true; + this.reading = false; + + this.#address = undefined; + + this.#backlog = undefined; + this.#connections = 0; + this.#acceptBackoffDelay = undefined; + + if (this.provider === providerType.PIPESERVERWRAP) { + try { + this.#listener.close(); + } catch { + // listener already closed + } + } + + return LibuvStreamWrap.prototype._onClose.call(this); } } @@ -127,6 +388,6 @@ export enum constants { SOCKET = socketType.SOCKET, SERVER = socketType.SERVER, IPC = socketType.IPC, - UV_READABLE, - UV_WRITABLE, + UV_READABLE = 1, + UV_WRITABLE = 2, } diff --git a/node/internal_binding/stream_wrap.ts b/node/internal_binding/stream_wrap.ts index 9d562e092b5a..f5b0e90492c4 100644 --- a/node/internal_binding/stream_wrap.ts +++ b/node/internal_binding/stream_wrap.ts @@ -87,8 +87,6 @@ export class LibuvStreamWrap extends HandleWrap { reading!: boolean; #reading = false; - #currentReads: Set> = new Set(); - #currentWrites: Set> = new Set(); destroyed = false; writeQueueSize = 0; bytesRead = 0; @@ -111,12 +109,7 @@ export class LibuvStreamWrap extends HandleWrap { readStart(): number { if (!this.#reading) { this.#reading = true; - const readPromise = this.#read(); - this.#currentReads.add(readPromise); - readPromise.then( - () => this.#currentReads.delete(readPromise), - () => this.#currentReads.delete(readPromise), - ); + this.#read(); } return 0; @@ -138,15 +131,13 @@ export class LibuvStreamWrap extends HandleWrap { * @return An error status code. */ shutdown(req: ShutdownWrap): number { - (async () => { - const status = await this._onClose(); + const status = this._onClose(); - try { - req.oncomplete(status); - } catch { - // swallow callback error. - } - })(); + try { + req.oncomplete(status); + } catch { + // swallow callback error. + } return 0; } @@ -167,12 +158,7 @@ export class LibuvStreamWrap extends HandleWrap { * @return An error status code. */ writeBuffer(req: WriteWrap, data: Uint8Array): number { - const currentWrite = this.#write(req, data); - this.#currentWrites.add(currentWrite); - currentWrite.then( - () => this.#currentWrites.delete(currentWrite), - () => this.#currentWrites.delete(currentWrite), - ); + this.#write(req, data); return 0; } @@ -185,13 +171,32 @@ export class LibuvStreamWrap extends HandleWrap { * @return An error status code. */ writev( - _req: WriteWrap, - // deno-lint-ignore no-explicit-any - _chunks: any, - _allBuffers: boolean, + req: WriteWrap, + chunks: Buffer[] | (string | Buffer)[], + allBuffers: boolean, ): number { - // TODO(cmorten) - notImplemented("LibuvStreamWrap.prototype.writev"); + const count = allBuffers ? chunks.length : chunks.length >> 1; + const buffers: Buffer[] = new Array(count); + + if (!allBuffers) { + for (let i = 0; i < count; i++) { + const chunk = chunks[i * 2]; + + if (Buffer.isBuffer(chunk)) { + buffers[i] = chunk; + } + + // String chunk + const encoding: string = chunks[i * 2 + 1] as string; + buffers[i] = Buffer.from(chunk as string, encoding); + } + } else { + for (let i = 0; i < count; i++) { + buffers[i] = chunks[i] as Buffer; + } + } + + return this.writeBuffer(req, Buffer.concat(buffers)); } /** @@ -231,7 +236,7 @@ export class LibuvStreamWrap extends HandleWrap { return this.writeBuffer(req, buffer); } - override async _onClose(): Promise { + override _onClose(): number { let status = 0; this.#reading = false; @@ -241,9 +246,6 @@ export class LibuvStreamWrap extends HandleWrap { status = codeMap.get("ENOTCONN")!; } - await Promise.allSettled(this.#currentWrites); - await Promise.allSettled(this.#currentReads); - return status; } @@ -268,6 +270,11 @@ export class LibuvStreamWrap extends HandleWrap { e instanceof Deno.errors.BadResource ) { nread = codeMap.get("EOF")!; + } else if ( + e instanceof Deno.errors.ConnectionReset || + e instanceof Deno.errors.ConnectionAborted + ) { + nread = codeMap.get("ECONNRESET")!; } else { nread = codeMap.get("UNKNOWN")!; } @@ -294,12 +301,7 @@ export class LibuvStreamWrap extends HandleWrap { } if (nread >= 0 && this.#reading) { - const readPromise = this.#read(); - this.#currentReads.add(readPromise); - readPromise.then( - () => this.#currentReads.delete(readPromise), - () => this.#currentReads.delete(readPromise), - ); + this.#read(); } } @@ -312,11 +314,19 @@ export class LibuvStreamWrap extends HandleWrap { const { byteLength } = data; try { - // TODO(cmorten): somewhat over simplifying what Node does. await writeAll(this[kStreamBaseField]!, data); - } catch { + } catch (e) { + let status: number; + // TODO(cmorten): map err to status codes - const status = codeMap.get("UNKNOWN")!; + if ( + e instanceof Deno.errors.BadResource || + e instanceof Deno.errors.BrokenPipe + ) { + status = codeMap.get("EBADF")!; + } else { + status = codeMap.get("UNKNOWN")!; + } try { req.oncomplete(status); diff --git a/node/internal_binding/tcp_wrap.ts b/node/internal_binding/tcp_wrap.ts index 337566ee9530..4ec93c403bad 100644 --- a/node/internal_binding/tcp_wrap.ts +++ b/node/internal_binding/tcp_wrap.ts @@ -34,6 +34,11 @@ import { codeMap } from "./uv.ts"; import { delay } from "../../async/mod.ts"; import { kStreamBaseField } from "./stream_wrap.ts"; import { isIP } from "../internal/net.ts"; +import { + ceilPowOf2, + INITIAL_ACCEPT_BACKOFF_DELAY, + MAX_ACCEPT_BACKOFF_DELAY, +} from "./_listen.ts"; import * as DenoUnstable from "../../_deno_unstable.ts"; /** The type of TCP socket. */ @@ -44,26 +49,10 @@ enum socketType { interface AddressInfo { address: string; - family?: string; + family?: number; port: number; } -/** Initial backoff delay of 5ms following a temporary accept failure. */ -const INITIAL_ACCEPT_BACKOFF_DELAY = 5; - -/** Max backoff delay of 1s following a temporary accept failure. */ -const MAX_ACCEPT_BACKOFF_DELAY = 1000; - -/** - * @param n Number to act on. - * @return The number rounded up to the nearest power of 2. - */ -function _ceilPowOf2(n: number) { - const roundPowOf2 = 1 << 31 - Math.clz32(n); - - return roundPowOf2 < n ? roundPowOf2 * 2 : roundPowOf2; -} - export class TCPConnectWrap extends AsyncWrap { oncomplete!: ( status: number, @@ -96,7 +85,7 @@ export class TCP extends ConnectionWrap { #port?: number; #remoteAddress?: string; - #remoteFamily?: string; + #remoteFamily?: number; #remotePort?: number; #backlog?: number; @@ -142,7 +131,7 @@ export class TCP extends ConnectionWrap { const remoteAddr = conn.remoteAddr as Deno.NetAddr; this.#remoteAddress = remoteAddr.hostname; this.#remotePort = remoteAddr.port; - this.#remoteFamily = isIP(remoteAddr.hostname) === 6 ? "IPv6" : "IPv4"; + this.#remoteFamily = isIP(remoteAddr.hostname); } } @@ -200,11 +189,11 @@ export class TCP extends ConnectionWrap { /** * Listen for new connections. - * @param backlog + * @param backlog The maximum length of the queue of pending connections. * @return An error status code. */ listen(backlog: number): number { - this.#backlog = _ceilPowOf2(backlog + 1); + this.#backlog = ceilPowOf2(backlog + 1); const listenOptions = { hostname: this.#address!, @@ -242,6 +231,7 @@ export class TCP extends ConnectionWrap { DenoUnstable.ListenerRef(this.#listener); } } + override unref() { if (this.#listener) { DenoUnstable.ListenerUnref(this.#listener); @@ -255,14 +245,15 @@ export class TCP extends ConnectionWrap { */ getsockname(sockname: Record | AddressInfo): number { if ( - typeof this.#address === "undefined" || typeof this.#port === "undefined" + typeof this.#address === "undefined" || + typeof this.#port === "undefined" ) { return codeMap.get("EADDRNOTAVAIL")!; } sockname.address = this.#address; sockname.port = this.#port; - sockname.family = isIP(this.#address) === 6 ? "IPv6" : "IPv4"; + sockname.family = isIP(this.#address); return 0; } @@ -325,17 +316,21 @@ export class TCP extends ConnectionWrap { * Bind to an IPv4 or IPv6 address. * @param address The hostname to bind to. * @param port The port to bind to - * @param flags + * @param _flags * @return An error status code. */ #bind(address: string, port: number, _flags: number): number { - // Deno doesn't currently separate bind from connect. For now we noop under - // the assumption we will connect shortly. - // REF: https://doc.deno.land/builtin/stable#Deno.connect + // Deno doesn't currently separate bind from connect etc. + // REF: + // - https://doc.deno.land/deno/stable/~/Deno.connect + // - https://doc.deno.land/deno/stable/~/Deno.listen // // This also means we won't be connecting from the specified local address // and port as providing these is not an option in Deno. - // REF: https://doc.deno.land/builtin/stable#Deno.ConnectOptions + // REF: + // - https://doc.deno.land/deno/stable/~/Deno.ConnectOptions + // - https://doc.deno.land/deno/stable/~/Deno.ListenOptions + this.#address = address; this.#port = port; @@ -352,7 +347,7 @@ export class TCP extends ConnectionWrap { #connect(req: TCPConnectWrap, address: string, port: number): number { this.#remoteAddress = address; this.#remotePort = port; - this.#remoteFamily = isIP(address) === 6 ? "IPv6" : "IPv4"; + this.#remoteFamily = isIP(address); const connectOptions: Deno.ConnectOptions = { hostname: address, @@ -360,27 +355,30 @@ export class TCP extends ConnectionWrap { transport: "tcp", }; - Deno.connect(connectOptions).then((conn: Deno.Conn) => { - // Incorrect / backwards, but correcting the local address and port with - // what was actually used given we can't actually specify these in Deno. - const localAddr = conn.localAddr as Deno.NetAddr; - this.#address = req.localAddress = localAddr.hostname; - this.#port = req.localPort = localAddr.port; - this[kStreamBaseField] = conn; - - try { - this.afterConnect(req, 0); - } catch { - // swallow callback errors. - } - }, () => { - try { - // TODO(cmorten): correct mapping of connection error to status code. - this.afterConnect(req, codeMap.get("ECONNREFUSED")!); - } catch { - // swallow callback errors. - } - }); + Deno.connect(connectOptions).then( + (conn: Deno.Conn) => { + // Incorrect / backwards, but correcting the local address and port with + // what was actually used given we can't actually specify these in Deno. + const localAddr = conn.localAddr as Deno.NetAddr; + this.#address = req.localAddress = localAddr.hostname; + this.#port = req.localPort = localAddr.port; + this[kStreamBaseField] = conn; + + try { + this.afterConnect(req, 0); + } catch { + // swallow callback errors. + } + }, + () => { + try { + // TODO(cmorten): correct mapping of connection error to status code. + this.afterConnect(req, codeMap.get("ECONNREFUSED")!); + } catch { + // swallow callback errors. + } + }, + ); return 0; } @@ -455,8 +453,7 @@ export class TCP extends ConnectionWrap { } /** Handle server closure. */ - override async _onClose(): Promise { - // TODO(cmorten): this isn't great + override _onClose(): number { this.#closed = true; this.reading = false; @@ -479,6 +476,6 @@ export class TCP extends ConnectionWrap { } } - return await LibuvStreamWrap.prototype._onClose.call(this); + return LibuvStreamWrap.prototype._onClose.call(this); } } diff --git a/node/net.ts b/node/net.ts index a1e1730b8e14..e65d07fa9021 100644 --- a/node/net.ts +++ b/node/net.ts @@ -42,7 +42,7 @@ import { ERR_SOCKET_CLOSED, errnoException, exceptionWithHostPort, - NodeError, + genericNodeError, uvExceptionWithHostPort, } from "./internal/errors.ts"; import type { ErrnoException } from "./internal/errors.ts"; @@ -67,7 +67,7 @@ import { DTRACE_NET_STREAM_END, } from "./internal/dtrace.ts"; import { Buffer } from "./buffer.ts"; -import type { LookupOneOptions } from "./dns.ts"; +import type { LookupOneOptions } from "./internal/dns/utils.ts"; import { validateAbortSignal, validateFunction, @@ -211,7 +211,7 @@ interface IpcSocketConnectOptions extends ConnectOptions { type SocketConnectOptions = TcpSocketConnectOptions | IpcSocketConnectOptions; function _getNewAsyncId(handle?: Handle): number { - return (!handle || typeof handle.getAsyncId !== "function") + return !handle || typeof handle.getAsyncId !== "function" ? newAsyncId() : handle.getAsyncId(); } @@ -227,7 +227,7 @@ const _noop = (_arrayBuffer: Uint8Array, _nread: number): undefined => { }; function _toNumber(x: unknown): number | false { - return (x = Number(x)) >= 0 ? x as number : false; + return (x = Number(x)) >= 0 ? (x as number) : false; } function _isPipeName(s: unknown): s is string { @@ -240,15 +240,11 @@ function _createHandle(fd: number, isServer: boolean): Handle { const type = guessHandleType(fd); if (type === "PIPE") { - return new Pipe( - isServer ? PipeConstants.SERVER : PipeConstants.SOCKET, - ); + return new Pipe(isServer ? PipeConstants.SERVER : PipeConstants.SOCKET); } if (type === "TCP") { - return new TCP( - isServer ? TCPConstants.SERVER : TCPConstants.SOCKET, - ); + return new TCP(isServer ? TCPConstants.SERVER : TCPConstants.SOCKET); } throw new ERR_INVALID_FD_TYPE(type); @@ -467,7 +463,7 @@ function _internalConnect( req.oncomplete = _afterConnect; req.address = address; - err = (socket._handle as Pipe).connect(req, address, _afterConnect); + err = (socket._handle as Pipe).connect(req, address); } if (err) { @@ -495,7 +491,7 @@ function _writeAfterFIN( | BufferEncoding | null | ((error: Error | null | undefined) => void), - cb?: ((error: Error | null | undefined) => void), + cb?: (error: Error | null | undefined) => void, ): boolean { if (!this.writableEnded) { return Duplex.prototype.write.call( @@ -512,21 +508,20 @@ function _writeAfterFIN( encoding = null; } - const err = new NodeError( - "EPIPE", + const err = genericNodeError( "This socket has been ended by the other party", + { code: "EPIPE" }, ); if (typeof cb === "function") { - defaultTriggerAsyncIdScope( - this[asyncIdSymbol], - nextTick, - cb, - err, - ); + defaultTriggerAsyncIdScope(this[asyncIdSymbol], nextTick, cb, err); } - this.destroy(err); + if (this._server) { + nextTick(() => this.destroy(err)); + } else { + this.destroy(err); + } return false; } @@ -614,24 +609,20 @@ function _lookupAndConnect( // If host is an IP, skip performing a lookup const addressType = isIP(host); if (addressType) { - defaultTriggerAsyncIdScope( - self[asyncIdSymbol], - nextTick, - () => { - if (self.connecting) { - defaultTriggerAsyncIdScope( - self[asyncIdSymbol], - _internalConnect, - self, - host, - port, - addressType, - localAddress, - localPort, - ); - } - }, - ); + defaultTriggerAsyncIdScope(self[asyncIdSymbol], nextTick, () => { + if (self.connecting) { + defaultTriggerAsyncIdScope( + self[asyncIdSymbol], + _internalConnect, + self, + host, + port, + addressType, + localAddress, + localPort, + ); + } + }); return; } @@ -722,9 +713,9 @@ function _afterShutdown(this: ShutdownWrap) { this.callback(); } -function _emitCloseNT(socket: Socket) { +function _emitCloseNT(s: Socket | Server) { debug("SERVER: emit close"); - socket.emit("close"); + s.emit("close"); } /** @@ -801,7 +792,8 @@ export class Socket extends Duplex { const onread = options.onread; if ( - onread !== null && typeof onread === "object" && + onread !== null && + typeof onread === "object" && (isUint8Array(onread.buffer) || typeof onread.buffer === "function") && typeof onread.callback === "function" ) { @@ -944,7 +936,9 @@ export class Socket extends Duplex { */ override pause(): this { if ( - this[kBuffer] && !this.connecting && this._handle && + this[kBuffer] && + !this.connecting && + this._handle && this._handle.reading ) { this._handle.reading = false; @@ -968,7 +962,9 @@ export class Socket extends Duplex { */ override resume(): this { if ( - this[kBuffer] && !this.connecting && this._handle && + this[kBuffer] && + !this.connecting && + this._handle && !this._handle.reading ) { _tryReadStart(this); @@ -1034,7 +1030,8 @@ export class Socket extends Duplex { const newValue = noDelay === undefined ? true : !!noDelay; if ( - "setNoDelay" in this._handle && this._handle.setNoDelay && + "setNoDelay" in this._handle && + this._handle.setNoDelay && newValue !== this[kSetNoDelay] ) { this[kSetNoDelay] = newValue; @@ -1236,7 +1233,7 @@ export class Socket extends Duplex { * The string representation of the remote IP family. `"IPv4"` or `"IPv6"`. */ get remoteFamily(): string | undefined { - return this._getpeername().family; + return `IPv${this._getpeername().family}`; } /** @@ -1298,7 +1295,9 @@ export class Socket extends Duplex { size?: number, ): string | Uint8Array | Buffer | null | undefined { if ( - this[kBuffer] && !this.connecting && this._handle && + this[kBuffer] && + !this.connecting && + this._handle && !this._handle.reading ) { _tryReadStart(this); @@ -1331,7 +1330,7 @@ export class Socket extends Duplex { // The user has called .end(), and all the bytes have been // sent out to the other side. // deno-lint-ignore no-explicit-any - override _final = (cb: any): any => { + override _final(cb: any): any { // If still connecting - defer handling `_final` until 'connect' will happen if (this.pending) { debug("_final: not yet connected"); @@ -1356,7 +1355,7 @@ export class Socket extends Duplex { } else if (err !== 0) { return cb(errnoException(err, "shutdown")); } - }; + } _onTimeout() { const handle = this._handle; @@ -1389,10 +1388,7 @@ export class Socket extends Duplex { } } - override _destroy( - exception: Error | null, - cb: (err: Error | null) => void, - ) { + override _destroy(exception: Error | null, cb: (err: Error | null) => void) { debug("destroy"); this.connecting = false; @@ -1409,15 +1405,10 @@ export class Socket extends Duplex { this[kBytesRead] = this._handle.bytesRead; this[kBytesWritten] = this._handle.bytesWritten; - // deno-lint-ignore no-this-alias - const that = this; - this._handle.close(() => { - // Close is async, so we differ from Node here in explicitly waiting for - // the callback to have fired. - that._handle!.onread = _noop; - that._handle = null; - that._sockname = undefined; + this._handle!.onread = _noop; + this._handle = null; + this._sockname = undefined; cb(exception); @@ -1628,8 +1619,11 @@ interface ServerOptions { function _isServerSocketOptions( options: unknown, ): options is null | undefined | ServerOptions { - return options === null || typeof options === "undefined" || - typeof options === "object"; + return ( + options === null || + typeof options === "undefined" || + typeof options === "object" + ); } function _isConnectionListener( @@ -1773,19 +1767,23 @@ export function _createServerHandle( debug("bind to", address || "any"); if (!address) { + // TODO: differs from Node which tries to bind to IPv6 first when no + // address is provided. + // + // Forcing IPv4 as a workaround for Deno not aligning with Node on + // implicit binding on Windows. + // + // REF: https://github.com/denoland/deno/issues/10762 + // Try binding to ipv6 first - err = (handle as TCP).bind6( - DEFAULT_IPV6_ADDR, - port ?? 0, - flags ?? 0, - ); + // err = (handle as TCP).bind6(DEFAULT_IPV6_ADDR, port ?? 0, flags ?? 0); - if (err) { - handle.close(); + // if (err) { + // handle.close(); - // Fallback to ipv4 - return _createServerHandle(DEFAULT_IPV4_ADDR, port, 4, null, flags); - } + // Fallback to ipv4 + return _createServerHandle(DEFAULT_IPV4_ADDR, port, 4, null, flags); + // } } else if (addressType === 6) { err = (handle as TCP).bind6(address, port ?? 0, flags ?? 0); } else { @@ -1841,6 +1839,8 @@ function _onconnection(this: any, err: number, clientHandle?: Handle) { writable: true, }); + // TODO: implement noDelay and setKeepAlive + self._connections++; socket.server = self; socket._server = self; @@ -1871,16 +1871,23 @@ function _setupListenHandle( // Try to bind to the unspecified IPv6 address, see if IPv6 is available if (!address && typeof fd !== "number") { - rval = _createServerHandle(DEFAULT_IPV6_ADDR, port, 6, fd, flags); - - if (typeof rval === "number") { - rval = null; - address = DEFAULT_IPV4_ADDR; - addressType = 4; - } else { - address = DEFAULT_IPV6_ADDR; - addressType = 6; - } + // TODO: differs from Node which tries to bind to IPv6 first when no + // address is provided. + // + // Forcing IPv4 as a workaround for Deno not aligning with Node on + // implicit binding on Windows. + // + // REF: https://github.com/denoland/deno/issues/10762 + // rval = _createServerHandle(DEFAULT_IPV6_ADDR, port, 6, fd, flags); + + // if (typeof rval === "number") { + // rval = null; + address = DEFAULT_IPV4_ADDR; + addressType = 4; + // } else { + // address = DEFAULT_IPV6_ADDR; + // addressType = 6; + // } } if (rval === null) { @@ -2077,7 +2084,7 @@ export class Server extends EventEmitter { const backlogFromArgs: number = // (handle, backlog) or (path, backlog) or (port, backlog) _toNumber(args.length > 1 && args[1]) || - _toNumber(args.length > 2 && args[2]) as number; // (port, host, backlog) + (_toNumber(args.length > 2 && args[2]) as number); // (port, host, backlog) // deno-lint-ignore no-explicit-any options = (options as any)._handle || (options as any).handle || options; @@ -2107,7 +2114,8 @@ export class Server extends EventEmitter { // or (options[, cb]) where options.port is explicitly set as undefined or // null, bind to an arbitrary unused port if ( - args.length === 0 || typeof args[0] === "function" || + args.length === 0 || + typeof args[0] === "function" || (typeof options.port === "undefined" && "port" in options) || options.port === null ) { @@ -2153,7 +2161,7 @@ export class Server extends EventEmitter { // (path[, backlog][, cb]) or (options[, cb]) // where path or options.path is a UNIX domain socket or Windows pipe if (options.path && _isPipeName(options.path)) { - const pipeName = this._pipeName = options.path; + const pipeName = (this._pipeName = options.path); backlog = options.backlog || backlogFromArgs; _listenInCluster( @@ -2196,7 +2204,7 @@ export class Server extends EventEmitter { return this; } - if (!(("port" in options) || ("path" in options))) { + if (!("port" in options || "path" in options)) { throw new ERR_INVALID_ARG_VALUE( "options", options,