From 354dc7f6b76a58fcd95de4e33afc29b289bdf321 Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Fri, 10 Dec 2021 22:46:07 +0900 Subject: [PATCH 1/9] feat(node): add Timeout class --- node/_tools/config.json | 15 ++++ .../suites/parallel/test-timers-api-refs.js | 28 ++++++ .../suites/parallel/test-timers-args.js | 38 ++++++++ ...-timers-clear-null-does-not-throw-error.js | 18 ++++ ...imers-clear-object-does-not-throw-error.js | 15 ++++ ...imers-clear-timeout-interval-equivalent.js | 25 ++++++ .../parallel/test-timers-clearImmediate.js | 20 +++++ .../parallel/test-timers-non-integer-delay.js | 88 +++++++++++++++++++ ...-timers-same-timeout-wrong-list-deleted.js | 41 +++++++++ .../suites/parallel/test-timers-this.js | 57 ++++++++++++ .../test-timers-timeout-with-non-integer.js | 22 +++++ .../parallel/test-timers-to-primitive.js | 36 ++++++++ .../suites/parallel/test-timers-unref.js | 88 +++++++++++++++++++ .../suites/parallel/test-timers-user-call.js | 47 ++++++++++ .../parallel/test-timers-zero-timeout.js | 56 ++++++++++++ node/_tools/suites/parallel/test-timers.js | 88 +++++++++++++++++++ node/global.ts | 32 +++++++ node/internal/timers.js | 56 ++++++++++++ node/timers.ts | 72 ++++++++++++--- 19 files changed, 831 insertions(+), 11 deletions(-) create mode 100644 node/_tools/suites/parallel/test-timers-api-refs.js create mode 100644 node/_tools/suites/parallel/test-timers-args.js create mode 100644 node/_tools/suites/parallel/test-timers-clear-null-does-not-throw-error.js create mode 100644 node/_tools/suites/parallel/test-timers-clear-object-does-not-throw-error.js create mode 100644 node/_tools/suites/parallel/test-timers-clear-timeout-interval-equivalent.js create mode 100644 node/_tools/suites/parallel/test-timers-clearImmediate.js create mode 100644 node/_tools/suites/parallel/test-timers-non-integer-delay.js create mode 100644 node/_tools/suites/parallel/test-timers-same-timeout-wrong-list-deleted.js create mode 100644 node/_tools/suites/parallel/test-timers-this.js create mode 100644 node/_tools/suites/parallel/test-timers-timeout-with-non-integer.js create mode 100644 node/_tools/suites/parallel/test-timers-to-primitive.js create mode 100644 node/_tools/suites/parallel/test-timers-unref.js create mode 100644 node/_tools/suites/parallel/test-timers-user-call.js create mode 100644 node/_tools/suites/parallel/test-timers-zero-timeout.js create mode 100644 node/_tools/suites/parallel/test-timers.js create mode 100644 node/internal/timers.js diff --git a/node/_tools/config.json b/node/_tools/config.json index b63cddcf8415..d18d85c6f986 100644 --- a/node/_tools/config.json +++ b/node/_tools/config.json @@ -367,6 +367,21 @@ "test-stream3-cork-uncork.js", "test-stream3-pause-then-read.js", "test-streams-highwatermark.js", + "test-timers-api-refs.js", + "test-timers-args.js", + "test-timers-clear-null-does-not-throw-error.js", + "test-timers-clear-object-does-not-throw-error.js", + "test-timers-clear-timeout-interval-equivalent.js", + "test-timers-clearImmediate.js", + "test-timers-non-integer-delay.js", + "test-timers-same-timeout-wrong-list-deleted.js", + "test-timers-this.js", + "test-timers-timeout-with-non-integer.js", + "test-timers-to-primitive.js", + "test-timers-unref.js", + "test-timers-user-call.js", + "test-timers-zero-timeout.js", + "test-timers.js", "test-url-fileurltopath.js", "test-url-format-whatwg.js", "test-url-parse-format.js", diff --git a/node/_tools/suites/parallel/test-timers-api-refs.js b/node/_tools/suites/parallel/test-timers-api-refs.js new file mode 100644 index 000000000000..5c653457b9c7 --- /dev/null +++ b/node/_tools/suites/parallel/test-timers-api-refs.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'); +const timers = require('timers'); + +// Delete global APIs to make sure they're not relied on by the internal timers +// code +delete global.setTimeout; +delete global.clearTimeout; +delete global.setInterval; +delete global.clearInterval; +delete global.setImmediate; +delete global.clearImmediate; + +const timeoutCallback = () => { timers.clearTimeout(timeout); }; +const timeout = timers.setTimeout(common.mustCall(timeoutCallback), 1); + +const intervalCallback = () => { timers.clearInterval(interval); }; +const interval = timers.setInterval(common.mustCall(intervalCallback), 1); + +const immediateCallback = () => { timers.clearImmediate(immediate); }; +const immediate = timers.setImmediate(immediateCallback); diff --git a/node/_tools/suites/parallel/test-timers-args.js b/node/_tools/suites/parallel/test-timers-args.js new file mode 100644 index 000000000000..27882a9ac1f2 --- /dev/null +++ b/node/_tools/suites/parallel/test-timers-args.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'; +require('../common'); +const assert = require('assert'); + +function range(n) { + return 'x'.repeat(n + 1).split('').map(function(_, i) { return i; }); +} + +function timeout(nargs) { + const args = range(nargs); + setTimeout.apply(null, [callback, 1].concat(args)); + + function callback() { + assert.deepStrictEqual([].slice.call(arguments), args); + if (nargs < 128) timeout(nargs + 1); + } +} + +function interval(nargs) { + const args = range(nargs); + const timer = setTimeout.apply(null, [callback, 1].concat(args)); + + function callback() { + clearInterval(timer); + assert.deepStrictEqual([].slice.call(arguments), args); + if (nargs < 128) interval(nargs + 1); + } +} + +timeout(0); +interval(0); diff --git a/node/_tools/suites/parallel/test-timers-clear-null-does-not-throw-error.js b/node/_tools/suites/parallel/test-timers-clear-null-does-not-throw-error.js new file mode 100644 index 000000000000..03ebc2fc24ff --- /dev/null +++ b/node/_tools/suites/parallel/test-timers-clear-null-does-not-throw-error.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 makes sure clearing timers with +// 'null' or no input does not throw error +clearInterval(null); +clearInterval(); +clearTimeout(null); +clearTimeout(); +clearImmediate(null); +clearImmediate(); diff --git a/node/_tools/suites/parallel/test-timers-clear-object-does-not-throw-error.js b/node/_tools/suites/parallel/test-timers-clear-object-does-not-throw-error.js new file mode 100644 index 000000000000..2f16eba86ed1 --- /dev/null +++ b/node/_tools/suites/parallel/test-timers-clear-object-does-not-throw-error.js @@ -0,0 +1,15 @@ +// 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 makes sure clearing timers with +// objects doesn't throw +clearImmediate({}); +clearTimeout({}); +clearInterval({}); diff --git a/node/_tools/suites/parallel/test-timers-clear-timeout-interval-equivalent.js b/node/_tools/suites/parallel/test-timers-clear-timeout-interval-equivalent.js new file mode 100644 index 000000000000..0f6d0675bd16 --- /dev/null +++ b/node/_tools/suites/parallel/test-timers-clear-timeout-interval-equivalent.js @@ -0,0 +1,25 @@ +// 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 makes sure that timers created with setTimeout can be disarmed by +// clearInterval and that timers created with setInterval can be disarmed by +// clearTimeout. +// +// This behavior is documented in the HTML Living Standard: +// +// * Refs: https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#dom-setinterval + +// Disarm interval with clearTimeout. +const interval = setInterval(common.mustNotCall(), 1); +clearTimeout(interval); + +// Disarm timeout with clearInterval. +const timeout = setTimeout(common.mustNotCall(), 1); +clearInterval(timeout); diff --git a/node/_tools/suites/parallel/test-timers-clearImmediate.js b/node/_tools/suites/parallel/test-timers-clearImmediate.js new file mode 100644 index 000000000000..a41ed6c454a4 --- /dev/null +++ b/node/_tools/suites/parallel/test-timers-clearImmediate.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 N = 3; + +function next() { + const fn = common.mustCall(() => clearImmediate(immediate)); + const immediate = setImmediate(fn); +} + +for (let i = 0; i < N; i++) { + next(); +} diff --git a/node/_tools/suites/parallel/test-timers-non-integer-delay.js b/node/_tools/suites/parallel/test-timers-non-integer-delay.js new file mode 100644 index 000000000000..6e80cd0f7583 --- /dev/null +++ b/node/_tools/suites/parallel/test-timers-non-integer-delay.js @@ -0,0 +1,88 @@ +// 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'); + +// This test makes sure that non-integer timer delays do not make the process +// hang. See https://github.com/joyent/node/issues/8065 and +// https://github.com/joyent/node/issues/8068 which have been fixed by +// https://github.com/joyent/node/pull/8073. +// +// If the process hangs, this test will make the tests suite timeout, +// otherwise it will exit very quickly (after 50 timers with a short delay +// fire). +// +// We have to set at least several timers with a non-integer delay to +// reproduce the issue. Sometimes, a timer with a non-integer delay will +// expire correctly. 50 timers has always been more than enough to reproduce +// it 100%. + +const TIMEOUT_DELAY = 1.1; +let N = 50; + +const interval = setInterval(common.mustCall(() => { + if (--N === 0) { + clearInterval(interval); + } +}, N), TIMEOUT_DELAY); + +// Test non-integer delay ordering +{ + const ordering = []; + + setTimeout(common.mustCall(() => { + ordering.push(1); + }), 1); + + setTimeout(common.mustCall(() => { + ordering.push(2); + }), 1.8); + + setTimeout(common.mustCall(() => { + ordering.push(3); + }), 1.1); + + setTimeout(common.mustCall(() => { + ordering.push(4); + }), 1); + + setTimeout(common.mustCall(() => { + const expected = [1, 2, 3, 4]; + + assert.deepStrictEqual( + ordering, + expected, + `Non-integer delay ordering should be ${expected}, but got ${ordering}` + ); + + // 2 should always be last of these delays due to ordering guarantees by + // the implementation. + }), 2); +} diff --git a/node/_tools/suites/parallel/test-timers-same-timeout-wrong-list-deleted.js b/node/_tools/suites/parallel/test-timers-same-timeout-wrong-list-deleted.js new file mode 100644 index 000000000000..a42340b018bb --- /dev/null +++ b/node/_tools/suites/parallel/test-timers-same-timeout-wrong-list-deleted.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'; + +// This is a regression test for https://github.com/nodejs/node/issues/7722. +// +// When nested timers have the same timeout, calling clearTimeout on the +// older timer after it has fired causes the list the newer timer is in +// to be deleted. Since the newer timer was not cleared, it still blocks +// the event loop completing for the duration of its timeout, however, since +// no reference exists to it in its list, it cannot be canceled and its +// callback is not called when the timeout elapses. + +const common = require('../common'); + +const TIMEOUT = common.platformTimeout(100); + +const handle1 = setTimeout(common.mustCall(function() { + // Cause the old TIMEOUT list to be deleted + clearTimeout(handle1); + + // Cause a new list with the same key (TIMEOUT) to be created for this timer + const handle2 = setTimeout(common.mustNotCall(), TIMEOUT); + + setTimeout(common.mustCall(function() { + // Attempt to cancel the second timer. Fix for this bug will keep the + // newer timer from being dereferenced by keeping its list from being + // erroneously deleted. If we are able to cancel the timer successfully, + // the bug is fixed. + clearTimeout(handle2); + }), 1); + + // When this callback completes, `listOnTimeout` should now look at the + // correct list and refrain from removing the new TIMEOUT list which + // contains the reference to the newer timer. +}), TIMEOUT); diff --git a/node/_tools/suites/parallel/test-timers-this.js b/node/_tools/suites/parallel/test-timers-this.js new file mode 100644 index 000000000000..7e7c5dcfe945 --- /dev/null +++ b/node/_tools/suites/parallel/test-timers-this.js @@ -0,0 +1,57 @@ +// 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 immediateHandler = setImmediate(common.mustCall(function() { + assert.strictEqual(this, immediateHandler); +})); + +const immediateArgsHandler = setImmediate(common.mustCall(function() { + assert.strictEqual(this, immediateArgsHandler); +}), 'args ...'); + +const intervalHandler = setInterval(common.mustCall(function() { + clearInterval(intervalHandler); + assert.strictEqual(this, intervalHandler); +}), 1); + +const intervalArgsHandler = setInterval(common.mustCall(function() { + clearInterval(intervalArgsHandler); + assert.strictEqual(this, intervalArgsHandler); +}), 1, 'args ...'); + +const timeoutHandler = setTimeout(common.mustCall(function() { + assert.strictEqual(this, timeoutHandler); +}), 1); + +const timeoutArgsHandler = setTimeout(common.mustCall(function() { + assert.strictEqual(this, timeoutArgsHandler); +}), 1, 'args ...'); diff --git a/node/_tools/suites/parallel/test-timers-timeout-with-non-integer.js b/node/_tools/suites/parallel/test-timers-timeout-with-non-integer.js new file mode 100644 index 000000000000..e49a3ae23c5e --- /dev/null +++ b/node/_tools/suites/parallel/test-timers-timeout-with-non-integer.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'); + +/** + * This test is for https://github.com/nodejs/node/issues/24203 + */ +let count = 50; +const time = 1.00000000000001; +const exec = common.mustCall(() => { + if (--count === 0) { + return; + } + setTimeout(exec, time); +}, count); +exec(); diff --git a/node/_tools/suites/parallel/test-timers-to-primitive.js b/node/_tools/suites/parallel/test-timers-to-primitive.js new file mode 100644 index 000000000000..5d97c67a08f3 --- /dev/null +++ b/node/_tools/suites/parallel/test-timers-to-primitive.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 + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +[ + setTimeout(common.mustNotCall(), 1), + setInterval(common.mustNotCall(), 1), +].forEach((timeout) => { + assert.strictEqual(Number.isNaN(+timeout), false); + assert.strictEqual(+timeout, timeout[Symbol.toPrimitive]()); + assert.strictEqual(`${timeout}`, timeout[Symbol.toPrimitive]().toString()); + assert.deepStrictEqual(Object.keys({ [timeout]: timeout }), [`${timeout}`]); + clearTimeout(+timeout); +}); + +{ + // Check that clearTimeout works with number id. + const timeout = setTimeout(common.mustNotCall(), 1); + const id = +timeout; + clearTimeout(id); +} + +{ + // Check that clearTimeout works with string id. + const timeout = setTimeout(common.mustNotCall(), 1); + const id = `${timeout}`; + clearTimeout(id); +} diff --git a/node/_tools/suites/parallel/test-timers-unref.js b/node/_tools/suites/parallel/test-timers-unref.js new file mode 100644 index 000000000000..3003cfef838f --- /dev/null +++ b/node/_tools/suites/parallel/test-timers-unref.js @@ -0,0 +1,88 @@ +// 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'); + +let unref_interval = false; +let unref_timer = false; +let checks = 0; + +const LONG_TIME = 10 * 1000; +const SHORT_TIME = 100; + +const timer = setTimeout(() => {}, 10); +assert.strictEqual(timer.hasRef(), true); +// Should not throw. +timer.unref().ref().unref(); +assert.strictEqual(timer.hasRef(), false); + +setInterval(() => {}, 10).unref().ref().unref(); + +setInterval(common.mustNotCall('Interval should not fire'), LONG_TIME).unref(); +setTimeout(common.mustNotCall('Timer should not fire'), LONG_TIME).unref(); + +const interval = setInterval(common.mustCall(() => { + unref_interval = true; + clearInterval(interval); +}), SHORT_TIME); +interval.unref(); + +setTimeout(common.mustCall(() => { + unref_timer = true; +}), SHORT_TIME).unref(); + +const check_unref = setInterval(() => { + if (checks > 5 || (unref_interval && unref_timer)) + clearInterval(check_unref); + checks += 1; +}, 100); + +{ + const timeout = + setTimeout(common.mustCall(() => { + timeout.unref(); + }), SHORT_TIME); +} + +{ + // Should not timeout the test + const timeout = + setInterval(() => timeout.unref(), SHORT_TIME); +} + +// Should not assert on args.Holder()->InternalFieldCount() > 0. +// See https://github.com/nodejs/node-v0.x-archive/issues/4261. +{ + const t = setInterval(() => {}, 1); + process.nextTick(t.unref.bind({})); + process.nextTick(t.unref.bind(t)); +} diff --git a/node/_tools/suites/parallel/test-timers-user-call.js b/node/_tools/suites/parallel/test-timers-user-call.js new file mode 100644 index 000000000000..3cd9269eb82c --- /dev/null +++ b/node/_tools/suites/parallel/test-timers-user-call.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 + +// Make sure `setTimeout()` and friends don't throw if the user-supplied +// function has .call() and .apply() monkey-patched to undesirable values. + +// Refs: https://github.com/nodejs/node/issues/12956 + +'use strict'; + +const common = require('../common'); + +{ + const fn = common.mustCall(10); + fn.call = 'not a function'; + fn.apply = 'also not a function'; + setTimeout(fn, 1); + setTimeout(fn, 1, 'oneArg'); + setTimeout(fn, 1, 'two', 'args'); + setTimeout(fn, 1, 'three', '(3)', 'args'); + setTimeout(fn, 1, 'more', 'than', 'three', 'args'); + + setImmediate(fn, 1); + setImmediate(fn, 1, 'oneArg'); + setImmediate(fn, 1, 'two', 'args'); + setImmediate(fn, 1, 'three', '(3)', 'args'); + setImmediate(fn, 1, 'more', 'than', 'three', 'args'); +} + +{ + const testInterval = (...args) => { + const fn = common.mustCall(() => { clearInterval(interval); }); + fn.call = 'not a function'; + fn.apply = 'also not a function'; + const interval = setInterval(fn, 1, ...args); + }; + + testInterval(); + testInterval('oneArg'); + testInterval('two', 'args'); + testInterval('three', '(3)', 'args'); + testInterval('more', 'than', 'three', 'args'); +} diff --git a/node/_tools/suites/parallel/test-timers-zero-timeout.js b/node/_tools/suites/parallel/test-timers-zero-timeout.js new file mode 100644 index 000000000000..6a3367517e3e --- /dev/null +++ b/node/_tools/suites/parallel/test-timers-zero-timeout.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 + +// 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'); + +// https://github.com/joyent/node/issues/2079 - zero timeout drops extra args +{ + setTimeout(common.mustCall(f), 0, 'foo', 'bar', 'baz'); + setTimeout(() => {}, 0); + + function f(a, b, c) { + assert.strictEqual(a, 'foo'); + assert.strictEqual(b, 'bar'); + assert.strictEqual(c, 'baz'); + } +} + +{ + let ncalled = 3; + + const f = common.mustCall((a, b, c) => { + assert.strictEqual(a, 'foo'); + assert.strictEqual(b, 'bar'); + assert.strictEqual(c, 'baz'); + if (--ncalled === 0) clearTimeout(iv); + }, ncalled); + + const iv = setInterval(f, 0, 'foo', 'bar', 'baz'); +} diff --git a/node/_tools/suites/parallel/test-timers.js b/node/_tools/suites/parallel/test-timers.js new file mode 100644 index 000000000000..1a8f030dc8af --- /dev/null +++ b/node/_tools/suites/parallel/test-timers.js @@ -0,0 +1,88 @@ +// 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 inputs = [ + undefined, + null, + true, + false, + '', + [], + {}, + NaN, + +Infinity, + -Infinity, + (1.0 / 0.0), // sanity check + parseFloat('x'), // NaN + -10, + -1, + -0.5, + -0.1, + -0.0, + 0, + 0.0, + 0.1, + 0.5, + 1, + 1.0, + 2147483648, // Browser behavior: timeouts > 2^31-1 run on next tick + 12345678901234, // ditto +]; + +const timeouts = []; +const intervals = []; + +inputs.forEach((value, index) => { + setTimeout(() => { + timeouts[index] = true; + }, value); + + const handle = setInterval(() => { + clearInterval(handle); // Disarm timer or we'll never finish + intervals[index] = true; + }, value); +}); + +// All values in inputs array coerce to 1 ms. Therefore, they should all run +// before a timer set here for 2 ms. + +setTimeout(common.mustCall(() => { + // Assert that all other timers have run + inputs.forEach((value, index) => { + assert(timeouts[index]); + assert(intervals[index]); + }); +}), 2); + +// Test 10 ms timeout separately. +setTimeout(common.mustCall(), 10); +setInterval(common.mustCall(function() { clearInterval(this); }), 10); diff --git a/node/global.ts b/node/global.ts index 5db33a28b123..f2cdf950b023 100644 --- a/node/global.ts +++ b/node/global.ts @@ -7,6 +7,10 @@ import timers from "./timers.ts"; type GlobalType = { process: typeof processModule; Buffer: typeof bufferModule; + setTimeout: typeof timers.setTimeout; + clearTimeout: typeof timers.clearTimeout; + setInterval: typeof timers.setInterval; + clearInterval: typeof timers.clearInterval; setImmediate: typeof timers.setImmediate; clearImmediate: typeof timers.clearImmediate; }; @@ -49,6 +53,34 @@ Object.defineProperty(globalThis, "Buffer", { configurable: true, }); +Object.defineProperty(globalThis, "setTimeout", { + value: timers.setTimeout, + enumerable: true, + writable: true, + configurable: true, +}); + +Object.defineProperty(globalThis, "clearTimeout", { + value: timers.clearTimeout, + enumerable: true, + writable: true, + configurable: true, +}); + +Object.defineProperty(globalThis, "setInterval", { + value: timers.setInterval, + enumerable: true, + writable: true, + configurable: true, +}); + +Object.defineProperty(globalThis, "clearInterval", { + value: timers.clearInterval, + enumerable: true, + writable: true, + configurable: true, +}); + Object.defineProperty(globalThis, "setImmediate", { value: timers.setImmediate, enumerable: true, diff --git a/node/internal/timers.js b/node/internal/timers.js new file mode 100644 index 000000000000..6336bc319c47 --- /dev/null +++ b/node/internal/timers.js @@ -0,0 +1,56 @@ +// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. + +import { notImplemented } from "../_utils.ts"; +import { inspect } from "./util/inspect.js"; + +// Timeout values > TIMEOUT_MAX are set to 1. +export const TIMEOUT_MAX = 2 ** 31 - 1; + +export const kTimerId = Symbol("timerId"); +const kRefed = Symbol("refed"); + +// Timer constructor function. +// The entire prototype is defined in lib/timers.js +export function Timeout(id) { + this[kTimerId] = id; + this[kRefed] = true; +} + +// Make sure the linked list only shows the minimal necessary information. +Timeout.prototype[inspect.custom] = function (_, options) { + return inspect(this, { + ...options, + // Only inspect one level. + depth: 0, + // It should not recurse. + customInspect: false, + }); +}; + +Timeout.prototype.refresh = function () { + notImplemented(); +}; + +Timeout.prototype.unref = function () { + if (this[kRefed]) { + this[kRefed] = false; + Deno.unrefTimer(this[kTimerId]); + } + return this; +}; + +Timeout.prototype.ref = function () { + if (!this[kRefed]) { + this[kRefed] = true; + Deno.refTimer(this[kTimerId]); + } + return this; +}; + +Timeout.prototype.hasRef = function () { + return this[kRefed]; +}; + +Timeout.prototype[Symbol.toPrimitive] = function () { + return this[kTimerId]; +}; diff --git a/node/timers.ts b/node/timers.ts index e81f7e76b4c3..abf4be8a1051 100644 --- a/node/timers.ts +++ b/node/timers.ts @@ -1,17 +1,67 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// TODO(bartlomieju): implement the 'NodeJS.Timeout' and 'NodeJS.Immediate' versions of the timers. + +import { kTimerId, Timeout, TIMEOUT_MAX } from "./internal/timers.js"; +import { validateCallback } from "./internal/validators.js"; +const setTimeout_ = globalThis.setTimeout; +const clearTimeout_ = globalThis.clearTimeout; +const setInterval_ = globalThis.setInterval; +const clearInterval_ = globalThis.clearInterval; +type TimerParams = Parameters; +export function setTimeout( + cb: TimerParams[0], + timeout?: TimerParams[1], + ...args: unknown[] +) { + validateCallback(cb); + if (typeof timeout === "number" && timeout > TIMEOUT_MAX) { + timeout = 1; + } + const timer = new Timeout(setTimeout_( + (...args: unknown[]) => { + cb.bind(timer)(...args); + }, + timeout, + ...args, + )); + return timer; +} +export function clearTimeout(timeout?: Timeout | number) { + if (timeout == null) { + return; + } + clearTimeout_(+timeout); +} +export function setInterval( + cb: TimerParams[0], + timeout?: TimerParams[1], + ...args: unknown[] +) { + validateCallback(cb); + if (typeof timeout === "number" && timeout > TIMEOUT_MAX) { + timeout = 1; + } + const timer = new Timeout(setInterval_( + (...args: unknown[]) => { + cb.bind(timer)(...args); + }, + timeout, + ...args, + )); + return timer; +} +export function clearInterval(timeout?: Timeout | number | string) { + if (timeout == null) { + return; + } + clearInterval_(+timeout); +} +// TODO(bartlomieju): implement the 'NodeJS.Immediate' versions of the timers. // https://github.com/DefinitelyTyped/DefinitelyTyped/blob/1163ead296d84e7a3c80d71e7c81ecbd1a130e9a/types/node/v12/globals.d.ts#L1120-L1131 -export const setTimeout = globalThis.setTimeout; -export const clearTimeout = globalThis.clearTimeout; -export const setInterval = globalThis.setInterval; -export const clearInterval = globalThis.clearInterval; export const setImmediate = ( - // deno-lint-ignore no-explicit-any - cb: (...args: any[]) => void, - // deno-lint-ignore no-explicit-any - ...args: any[] -): number => globalThis.setTimeout(cb, 0, ...args); -export const clearImmediate = globalThis.clearTimeout; + cb: (...args: unknown[]) => void, + ...args: unknown[] +): Timeout => setTimeout(cb, 0, ...args); +export const clearImmediate = clearTimeout; export default { setTimeout, From acd2b69c0da6c2a34ba793a3554ccb81dd401948 Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Sat, 11 Dec 2021 02:16:54 +0900 Subject: [PATCH 2/9] fix: fix lint error --- node/timers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/timers.ts b/node/timers.ts index abf4be8a1051..1b413ba108f0 100644 --- a/node/timers.ts +++ b/node/timers.ts @@ -1,6 +1,6 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { kTimerId, Timeout, TIMEOUT_MAX } from "./internal/timers.js"; +import { Timeout, TIMEOUT_MAX } from "./internal/timers.js"; import { validateCallback } from "./internal/validators.js"; const setTimeout_ = globalThis.setTimeout; const clearTimeout_ = globalThis.clearTimeout; From ea2a01e7608762148232f7fd35db2cf23fe53c4d Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Wed, 22 Dec 2021 15:42:45 +0900 Subject: [PATCH 3/9] chore: remove timer globals --- node/global.ts | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/node/global.ts b/node/global.ts index f2cdf950b023..5db33a28b123 100644 --- a/node/global.ts +++ b/node/global.ts @@ -7,10 +7,6 @@ import timers from "./timers.ts"; type GlobalType = { process: typeof processModule; Buffer: typeof bufferModule; - setTimeout: typeof timers.setTimeout; - clearTimeout: typeof timers.clearTimeout; - setInterval: typeof timers.setInterval; - clearInterval: typeof timers.clearInterval; setImmediate: typeof timers.setImmediate; clearImmediate: typeof timers.clearImmediate; }; @@ -53,34 +49,6 @@ Object.defineProperty(globalThis, "Buffer", { configurable: true, }); -Object.defineProperty(globalThis, "setTimeout", { - value: timers.setTimeout, - enumerable: true, - writable: true, - configurable: true, -}); - -Object.defineProperty(globalThis, "clearTimeout", { - value: timers.clearTimeout, - enumerable: true, - writable: true, - configurable: true, -}); - -Object.defineProperty(globalThis, "setInterval", { - value: timers.setInterval, - enumerable: true, - writable: true, - configurable: true, -}); - -Object.defineProperty(globalThis, "clearInterval", { - value: timers.clearInterval, - enumerable: true, - writable: true, - configurable: true, -}); - Object.defineProperty(globalThis, "setImmediate", { value: timers.setImmediate, enumerable: true, From e6960e9094415a46c99e5d87f2264ae8e9fc9d0e Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Wed, 22 Dec 2021 15:47:36 +0900 Subject: [PATCH 4/9] chore: exclude failing tests --- node/_tools/config.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/node/_tools/config.json b/node/_tools/config.json index d18d85c6f986..0b3827564e53 100644 --- a/node/_tools/config.json +++ b/node/_tools/config.json @@ -375,13 +375,9 @@ "test-timers-clearImmediate.js", "test-timers-non-integer-delay.js", "test-timers-same-timeout-wrong-list-deleted.js", - "test-timers-this.js", "test-timers-timeout-with-non-integer.js", - "test-timers-to-primitive.js", - "test-timers-unref.js", "test-timers-user-call.js", "test-timers-zero-timeout.js", - "test-timers.js", "test-url-fileurltopath.js", "test-url-format-whatwg.js", "test-url-parse-format.js", From 497eeb5efd642fd9aa9a33b29ce43f2209519193 Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Wed, 22 Dec 2021 16:10:49 +0900 Subject: [PATCH 5/9] refactor: merge internal/timers.js and internal/timers.ts --- node/_http_client.js | 2 +- node/_http_common.ts | 3 +- node/internal/http.ts | 2 +- node/internal/stream_base_commons.ts | 3 +- node/internal/timers.js | 38 +++++++++++++ node/internal/timers.ts | 84 ---------------------------- node/module_all.ts | 2 +- node/net.ts | 2 +- node/timers.ts | 7 +++ 9 files changed, 53 insertions(+), 90 deletions(-) delete mode 100644 node/internal/timers.ts diff --git a/node/_http_client.js b/node/_http_client.js index 448bc1607feb..5070072c3af4 100644 --- a/node/_http_client.js +++ b/node/_http_client.js @@ -27,7 +27,7 @@ import { ERR_UNESCAPED_CHARACTERS, } from "./_errors.ts"; import { validateInteger } from "./internal/validators.js"; -import { getTimerDuration } from "./internal/timers.ts"; +import { getTimerDuration } from "./internal/timers.js"; import { DTRACE_HTTP_CLIENT_REQUEST, DTRACE_HTTP_CLIENT_RESPONSE, diff --git a/node/_http_common.ts b/node/_http_common.ts index 11f0e518d8ed..aa8d94d2752a 100644 --- a/node/_http_common.ts +++ b/node/_http_common.ts @@ -131,7 +131,8 @@ export function freeParser( if (parsers.free(parser) === false) { // Make sure the parser's stack has unwound before deleting the // corresponding C++ object through .close(). - setImmediate(closeParserInstance, parser); + // deno-lint-ignore no-explicit-any + setImmediate(closeParserInstance as any, parser); } else { // Since the Parser destructor isn't going to run the destroy() callbacks // it needs to be triggered manually. diff --git a/node/internal/http.ts b/node/internal/http.ts index 1ced3b252f76..bf8899904f1d 100644 --- a/node/internal/http.ts +++ b/node/internal/http.ts @@ -1,7 +1,7 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. -import { setUnrefTimeout } from "./timers.ts"; +import { setUnrefTimeout } from "../timers.ts"; import { notImplemented } from "../_utils.ts"; let utcCache: string | undefined; diff --git a/node/internal/stream_base_commons.ts b/node/internal/stream_base_commons.ts index 13d041e375d3..ac6e049cc78d 100644 --- a/node/internal/stream_base_commons.ts +++ b/node/internal/stream_base_commons.ts @@ -30,7 +30,8 @@ import { } from "../internal_binding/stream_wrap.ts"; import { isUint8Array } from "./util/types.ts"; import { errnoException } from "../_errors.ts"; -import { getTimerDuration, kTimeout, setUnrefTimeout } from "./timers.ts"; +import { getTimerDuration, kTimeout } from "./timers.js"; +import { setUnrefTimeout } from "../timers.ts"; import { validateCallback } from "./validators.js"; import { codeMap } from "../internal_binding/uv.ts"; import { Buffer } from "../buffer.ts"; diff --git a/node/internal/timers.js b/node/internal/timers.js index 6336bc319c47..762fe85d3e88 100644 --- a/node/internal/timers.js +++ b/node/internal/timers.js @@ -2,11 +2,15 @@ import { notImplemented } from "../_utils.ts"; import { inspect } from "./util/inspect.js"; +import { validateNumber } from "./validators.js"; +import { ERR_OUT_OF_RANGE } from "../_errors.ts"; +import { emitWarning } from "../process.ts"; // Timeout values > TIMEOUT_MAX are set to 1. export const TIMEOUT_MAX = 2 ** 31 - 1; export const kTimerId = Symbol("timerId"); +export const kTimeout = Symbol("timeout"); const kRefed = Symbol("refed"); // Timer constructor function. @@ -54,3 +58,37 @@ Timeout.prototype.hasRef = function () { Timeout.prototype[Symbol.toPrimitive] = function () { return this[kTimerId]; }; + +/** + * @param {number} msecs + * @param {string} name + * @returns + */ +export function getTimerDuration(msecs, name) { + validateNumber(msecs, name); + + if (msecs < 0 || !Number.isFinite(msecs)) { + throw new ERR_OUT_OF_RANGE(name, "a non-negative finite number", msecs); + } + + // Ensure that msecs fits into signed int32 + if (msecs > TIMEOUT_MAX) { + emitWarning( + `${msecs} does not fit into a 32-bit signed integer.` + + `\nTimer duration was truncated to ${TIMEOUT_MAX}.`, + "TimeoutOverflowWarning", + ); + + return TIMEOUT_MAX; + } + + return msecs; +} + +export default { + TIMEOUT_MAX, + kTimerId, + kTimeout, + Timeout, + getTimerDuration, +}; diff --git a/node/internal/timers.ts b/node/internal/timers.ts deleted file mode 100644 index f8b41d1d23c5..000000000000 --- a/node/internal/timers.ts +++ /dev/null @@ -1,84 +0,0 @@ -// 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. - -import { validateCallback, validateNumber } from "./validators.js"; -import { ERR_OUT_OF_RANGE } from "../_errors.ts"; -import { emitWarning } from "../process.ts"; - -export const kTimeout = Symbol("timeout"); - -// Timeout values > TIMEOUT_MAX are set to 1. -export const TIMEOUT_MAX = 2 ** 31 - 1; - -class Timeout { - #timeoutId: number; - // deno-lint-ignore no-explicit-any - #callback: any; - #after: number; - - // deno-lint-ignore no-explicit-any - constructor(callback: any, after: number) { - this.#callback = callback; - this.#after = after; - this.#timeoutId = setTimeout(this.#callback, this.#after); - } - - refresh() { - clearTimeout(this.#timeoutId); - this.#timeoutId = setTimeout(this.#callback, this.#after); - } -} - -// deno-lint-ignore no-explicit-any -export function setUnrefTimeout(callback: any, after: number) { - // Type checking identical to setTimeout() - validateCallback(callback); - - return new Timeout(callback, after); -} - -export function getTimerDuration(msecs: number, name: string) { - validateNumber(msecs, name); - - if (msecs < 0 || !Number.isFinite(msecs)) { - throw new ERR_OUT_OF_RANGE(name, "a non-negative finite number", msecs); - } - - // Ensure that msecs fits into signed int32 - if (msecs > TIMEOUT_MAX) { - emitWarning( - `${msecs} does not fit into a 32-bit signed integer.` + - `\nTimer duration was truncated to ${TIMEOUT_MAX}.`, - "TimeoutOverflowWarning", - ); - - return TIMEOUT_MAX; - } - - return msecs; -} - -export default { - kTimeout, - TIMEOUT_MAX, - setUnrefTimeout, - getTimerDuration, -}; diff --git a/node/module_all.ts b/node/module_all.ts index 7018a7647f84..c91df400d91d 100644 --- a/node/module_all.ts +++ b/node/module_all.ts @@ -43,7 +43,7 @@ import internalStreamsAddAbortSignal from "./internal/streams/add-abort-signal.j import internalStreamsAddBufferList from "./internal/streams/buffer_list.js"; import internalStreamsState from "./internal/streams/state.js"; import internalTestBinding from "./internal/test/binding.ts"; -import internalTimers from "./internal/timers.ts"; +import internalTimers from "./internal/timers.js"; import internalUtilInspect from "./internal/util/inspect.js"; import net from "./net.ts"; import os from "./os.ts"; diff --git a/node/net.ts b/node/net.ts index 4ad68513ed66..96de80023865 100644 --- a/node/net.ts +++ b/node/net.ts @@ -59,7 +59,7 @@ import { writeGeneric, writevGeneric, } from "./internal/stream_base_commons.ts"; -import { kTimeout } from "./internal/timers.ts"; +import { kTimeout } from "./internal/timers.js"; import { nextTick } from "./_next_tick.ts"; import { DTRACE_NET_SERVER_CONNECTION, diff --git a/node/timers.ts b/node/timers.ts index 1b413ba108f0..955f762dcf3b 100644 --- a/node/timers.ts +++ b/node/timers.ts @@ -25,6 +25,13 @@ export function setTimeout( )); return timer; } +export function setUnrefTimeout( + cb: TimerParams[0], + timeout?: TimerParams[1], + ...args: unknown[] +) { + setTimeout(cb, timeout, ...args).unref(); +} export function clearTimeout(timeout?: Timeout | number) { if (timeout == null) { return; From 6f43d11ae7584fcb5babb0a38cad3314b46bbe31 Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Wed, 22 Dec 2021 16:30:36 +0900 Subject: [PATCH 6/9] chore: remove unused test files --- .../suites/parallel/test-timers-this.js | 57 ------------ .../parallel/test-timers-to-primitive.js | 36 -------- .../suites/parallel/test-timers-unref.js | 88 ------------------- node/_tools/suites/parallel/test-timers.js | 88 ------------------- 4 files changed, 269 deletions(-) delete mode 100644 node/_tools/suites/parallel/test-timers-this.js delete mode 100644 node/_tools/suites/parallel/test-timers-to-primitive.js delete mode 100644 node/_tools/suites/parallel/test-timers-unref.js delete mode 100644 node/_tools/suites/parallel/test-timers.js diff --git a/node/_tools/suites/parallel/test-timers-this.js b/node/_tools/suites/parallel/test-timers-this.js deleted file mode 100644 index 7e7c5dcfe945..000000000000 --- a/node/_tools/suites/parallel/test-timers-this.js +++ /dev/null @@ -1,57 +0,0 @@ -// 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 immediateHandler = setImmediate(common.mustCall(function() { - assert.strictEqual(this, immediateHandler); -})); - -const immediateArgsHandler = setImmediate(common.mustCall(function() { - assert.strictEqual(this, immediateArgsHandler); -}), 'args ...'); - -const intervalHandler = setInterval(common.mustCall(function() { - clearInterval(intervalHandler); - assert.strictEqual(this, intervalHandler); -}), 1); - -const intervalArgsHandler = setInterval(common.mustCall(function() { - clearInterval(intervalArgsHandler); - assert.strictEqual(this, intervalArgsHandler); -}), 1, 'args ...'); - -const timeoutHandler = setTimeout(common.mustCall(function() { - assert.strictEqual(this, timeoutHandler); -}), 1); - -const timeoutArgsHandler = setTimeout(common.mustCall(function() { - assert.strictEqual(this, timeoutArgsHandler); -}), 1, 'args ...'); diff --git a/node/_tools/suites/parallel/test-timers-to-primitive.js b/node/_tools/suites/parallel/test-timers-to-primitive.js deleted file mode 100644 index 5d97c67a08f3..000000000000 --- a/node/_tools/suites/parallel/test-timers-to-primitive.js +++ /dev/null @@ -1,36 +0,0 @@ -// 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'); - -[ - setTimeout(common.mustNotCall(), 1), - setInterval(common.mustNotCall(), 1), -].forEach((timeout) => { - assert.strictEqual(Number.isNaN(+timeout), false); - assert.strictEqual(+timeout, timeout[Symbol.toPrimitive]()); - assert.strictEqual(`${timeout}`, timeout[Symbol.toPrimitive]().toString()); - assert.deepStrictEqual(Object.keys({ [timeout]: timeout }), [`${timeout}`]); - clearTimeout(+timeout); -}); - -{ - // Check that clearTimeout works with number id. - const timeout = setTimeout(common.mustNotCall(), 1); - const id = +timeout; - clearTimeout(id); -} - -{ - // Check that clearTimeout works with string id. - const timeout = setTimeout(common.mustNotCall(), 1); - const id = `${timeout}`; - clearTimeout(id); -} diff --git a/node/_tools/suites/parallel/test-timers-unref.js b/node/_tools/suites/parallel/test-timers-unref.js deleted file mode 100644 index 3003cfef838f..000000000000 --- a/node/_tools/suites/parallel/test-timers-unref.js +++ /dev/null @@ -1,88 +0,0 @@ -// 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'); - -let unref_interval = false; -let unref_timer = false; -let checks = 0; - -const LONG_TIME = 10 * 1000; -const SHORT_TIME = 100; - -const timer = setTimeout(() => {}, 10); -assert.strictEqual(timer.hasRef(), true); -// Should not throw. -timer.unref().ref().unref(); -assert.strictEqual(timer.hasRef(), false); - -setInterval(() => {}, 10).unref().ref().unref(); - -setInterval(common.mustNotCall('Interval should not fire'), LONG_TIME).unref(); -setTimeout(common.mustNotCall('Timer should not fire'), LONG_TIME).unref(); - -const interval = setInterval(common.mustCall(() => { - unref_interval = true; - clearInterval(interval); -}), SHORT_TIME); -interval.unref(); - -setTimeout(common.mustCall(() => { - unref_timer = true; -}), SHORT_TIME).unref(); - -const check_unref = setInterval(() => { - if (checks > 5 || (unref_interval && unref_timer)) - clearInterval(check_unref); - checks += 1; -}, 100); - -{ - const timeout = - setTimeout(common.mustCall(() => { - timeout.unref(); - }), SHORT_TIME); -} - -{ - // Should not timeout the test - const timeout = - setInterval(() => timeout.unref(), SHORT_TIME); -} - -// Should not assert on args.Holder()->InternalFieldCount() > 0. -// See https://github.com/nodejs/node-v0.x-archive/issues/4261. -{ - const t = setInterval(() => {}, 1); - process.nextTick(t.unref.bind({})); - process.nextTick(t.unref.bind(t)); -} diff --git a/node/_tools/suites/parallel/test-timers.js b/node/_tools/suites/parallel/test-timers.js deleted file mode 100644 index 1a8f030dc8af..000000000000 --- a/node/_tools/suites/parallel/test-timers.js +++ /dev/null @@ -1,88 +0,0 @@ -// 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 inputs = [ - undefined, - null, - true, - false, - '', - [], - {}, - NaN, - +Infinity, - -Infinity, - (1.0 / 0.0), // sanity check - parseFloat('x'), // NaN - -10, - -1, - -0.5, - -0.1, - -0.0, - 0, - 0.0, - 0.1, - 0.5, - 1, - 1.0, - 2147483648, // Browser behavior: timeouts > 2^31-1 run on next tick - 12345678901234, // ditto -]; - -const timeouts = []; -const intervals = []; - -inputs.forEach((value, index) => { - setTimeout(() => { - timeouts[index] = true; - }, value); - - const handle = setInterval(() => { - clearInterval(handle); // Disarm timer or we'll never finish - intervals[index] = true; - }, value); -}); - -// All values in inputs array coerce to 1 ms. Therefore, they should all run -// before a timer set here for 2 ms. - -setTimeout(common.mustCall(() => { - // Assert that all other timers have run - inputs.forEach((value, index) => { - assert(timeouts[index]); - assert(intervals[index]); - }); -}), 2); - -// Test 10 ms timeout separately. -setTimeout(common.mustCall(), 10); -setInterval(common.mustCall(function() { clearInterval(this); }), 10); From c2da6195d04c3499edeafbf5ec5dc5524087fcc4 Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Wed, 22 Dec 2021 16:33:56 +0900 Subject: [PATCH 7/9] chore: add some missing license headers --- node/internal/timers.js | 1 + node/module_all.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/node/internal/timers.js b/node/internal/timers.js index 762fe85d3e88..8819ee6cd8e3 100644 --- a/node/internal/timers.js +++ b/node/internal/timers.js @@ -1,4 +1,5 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. import { notImplemented } from "../_utils.ts"; import { inspect } from "./util/inspect.js"; diff --git a/node/module_all.ts b/node/module_all.ts index c91df400d91d..da4c9c57550d 100644 --- a/node/module_all.ts +++ b/node/module_all.ts @@ -1,3 +1,4 @@ +// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. // This aliases are used in some node tests and represent a legacy alias // for the stream modules // deno-lint-ignore camelcase From aee779bb6db9873f7c2363ef81e813831bfd2de9 Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Tue, 18 Jan 2022 20:09:43 +0900 Subject: [PATCH 8/9] fix: fix an error in merge conflict resolution --- node/_http_client.js | 897 ------------------------------------------- 1 file changed, 897 deletions(-) delete mode 100644 node/_http_client.js diff --git a/node/_http_client.js b/node/_http_client.js deleted file mode 100644 index 5070072c3af4..000000000000 --- a/node/_http_client.js +++ /dev/null @@ -1,897 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// Copyright Joyent and Node contributors. All rights reserved. MIT license. - -import * as net from "./net.ts"; -import assert from "./internal/assert.js"; -import { once } from "./internal/util.js"; -import { - _checkIsHttpToken as checkIsHttpToken, - freeParser, - HTTPParser, - isLenient, - parsers, - prepareError, -} from "./_http_common.ts"; -import { OutgoingMessage } from "./_http_outgoing.ts"; -import Agent from "./_http_agent.js"; -import { Buffer } from "./buffer.ts"; -import { defaultTriggerAsyncIdScope } from "./internal/async_hooks.ts"; -import { urlToHttpOptions } from "./internal/url.ts"; -import { kNeedDrain, kOutHeaders } from "./internal/http.ts"; -import { - connResetException, - ERR_HTTP_HEADERS_SENT, - ERR_INVALID_ARG_TYPE, - ERR_INVALID_HTTP_TOKEN, - ERR_INVALID_PROTOCOL, - ERR_UNESCAPED_CHARACTERS, -} from "./_errors.ts"; -import { validateInteger } from "./internal/validators.js"; -import { getTimerDuration } from "./internal/timers.js"; -import { - DTRACE_HTTP_CLIENT_REQUEST, - DTRACE_HTTP_CLIENT_RESPONSE, -} from "./internal/dtrace.ts"; - -import { addAbortSignal, finished } from "./stream.ts"; -import { debuglog } from "./internal/util/debuglog.ts"; - -let debug = debuglog("http", (fn) => { - debug = fn; -}); - -const INVALID_PATH_REGEX = /[^\u0021-\u00ff]/; -const kError = Symbol("kError"); - -const kLenientAll = HTTPParser.kLenientAll | 0; -const kLenientNone = HTTPParser.kLenientNone | 0; - -function validateHost(host, name) { - if (host !== null && host !== undefined && typeof host !== "string") { - throw new ERR_INVALID_ARG_TYPE(`options.${name}`, [ - "string", - "undefined", - "null", - ], host); - } - return host; -} - -class HTTPClientAsyncResource { - constructor(type, req) { - this.type = type; - this.req = req; - } -} - -export function ClientRequest(input, options, cb) { - OutgoingMessage.call(this); - - if (typeof input === "string") { - const urlStr = input; - input = urlToHttpOptions(new URL(urlStr)); - } else if (input instanceof URL) { - // url.URL instance - input = urlToHttpOptions(input); - } else { - cb = options; - options = input; - input = null; - } - - if (typeof options === "function") { - cb = options; - options = input || {}; - } else { - options = Object.assign(input || {}, options); - } - - let agent = options.agent; - const defaultAgent = options._defaultAgent || Agent.globalAgent; - if (agent === false) { - agent = new defaultAgent.constructor(); - } else if (agent === null || agent === undefined) { - if (typeof options.createConnection !== "function") { - agent = defaultAgent; - } - // Explicitly pass through this statement as agent will not be used - // when createConnection is provided. - } else if (typeof agent.addRequest !== "function") { - throw new ERR_INVALID_ARG_TYPE("options.agent", [ - "Agent-like Object", - "undefined", - "false", - ], agent); - } - this.agent = agent; - - const protocol = options.protocol || defaultAgent.protocol; - let expectedProtocol = defaultAgent.protocol; - if (this.agent && this.agent.protocol) { - expectedProtocol = this.agent.protocol; - } - - if (options.path) { - const path = String(options.path); - if (INVALID_PATH_REGEX.test(path)) { - throw new ERR_UNESCAPED_CHARACTERS("Request path"); - } - } - - if (protocol !== expectedProtocol) { - throw new ERR_INVALID_PROTOCOL(protocol, expectedProtocol); - } - - const defaultPort = options.defaultPort || - (this.agent && this.agent.defaultPort); - - const port = options.port = options.port || defaultPort || 80; - const host = options.host = validateHost(options.hostname, "hostname") || - validateHost(options.host, "host") || "localhost"; - - const setHost = (options.setHost === undefined || Boolean(options.setHost)); - - this.socketPath = options.socketPath; - - if (options.timeout !== undefined) { - this.timeout = getTimerDuration(options.timeout, "timeout"); - } - - const signal = options.signal; - if (signal) { - addAbortSignal(signal, this); - } - let method = options.method; - const methodIsString = (typeof method === "string"); - if (method !== null && method !== undefined && !methodIsString) { - throw new ERR_INVALID_ARG_TYPE("options.method", "string", method); - } - - if (methodIsString && method) { - if (!checkIsHttpToken(method)) { - throw new ERR_INVALID_HTTP_TOKEN("Method", method); - } - method = this.method = method.toUpperCase(); - } else { - method = this.method = "GET"; - } - - const maxHeaderSize = options.maxHeaderSize; - if (maxHeaderSize !== undefined) { - validateInteger(maxHeaderSize, "maxHeaderSize", 0); - } - this.maxHeaderSize = maxHeaderSize; - - const insecureHTTPParser = options.insecureHTTPParser; - if ( - insecureHTTPParser !== undefined && - typeof insecureHTTPParser !== "boolean" - ) { - throw new ERR_INVALID_ARG_TYPE( - "options.insecureHTTPParser", - "boolean", - insecureHTTPParser, - ); - } - this.insecureHTTPParser = insecureHTTPParser; - - this.path = options.path || "/"; - if (cb) { - this.once("response", cb); - } - - if ( - method === "GET" || - method === "HEAD" || - method === "DELETE" || - method === "OPTIONS" || - method === "TRACE" || - method === "CONNECT" - ) { - this.useChunkedEncodingByDefault = false; - } else { - this.useChunkedEncodingByDefault = true; - } - - this._ended = false; - this.res = null; - this.aborted = false; - this.timeoutCb = null; - this.upgradeOrConnect = false; - this.parser = null; - this.maxHeadersCount = null; - this.reusedSocket = false; - this.host = host; - this.protocol = protocol; - - if (this.agent) { - // If there is an agent we should default to Connection:keep-alive, - // but only if the Agent will actually reuse the connection! - // If it's not a keepAlive agent, and the maxSockets==Infinity, then - // there's never a case where this socket will actually be reused - if (!this.agent.keepAlive && !Number.isFinite(this.agent.maxSockets)) { - this._last = true; - this.shouldKeepAlive = false; - } else { - this._last = false; - this.shouldKeepAlive = true; - } - } - - const headersArray = Array.isArray(options.headers); - if (!headersArray) { - if (options.headers) { - const keys = Object.keys(options.headers); - // Retain for(;;) loop for performance reasons - // Refs: https://github.com/nodejs/node/pull/30958 - for (let i = 0; i < keys.length; i++) { - const key = keys[i]; - this.setHeader(key, options.headers[key]); - } - } - - if (host && !this.getHeader("host") && setHost) { - let hostHeader = host; - - // For the Host header, ensure that IPv6 addresses are enclosed - // in square brackets, as defined by URI formatting - // https://tools.ietf.org/html/rfc3986#section-3.2.2 - const posColon = hostHeader.indexOf(":"); - if ( - posColon !== -1 && - hostHeader.includes(":", posColon + 1) && - hostHeader.charCodeAt(0) !== 91 /* '[' */ - ) { - hostHeader = `[${hostHeader}]`; - } - - if (port && +port !== defaultPort) { - hostHeader += ":" + port; - } - this.setHeader("Host", hostHeader); - } - - if (options.auth && !this.getHeader("Authorization")) { - this.setHeader( - "Authorization", - "Basic " + - Buffer.from(options.auth).toString("base64"), - ); - } - - if (this.getHeader("expect")) { - if (this._header) { - throw new ERR_HTTP_HEADERS_SENT("render"); - } - - this._storeHeader( - this.method + " " + this.path + " HTTP/1.1\r\n", - this[kOutHeaders], - ); - } - } else { - this._storeHeader( - this.method + " " + this.path + " HTTP/1.1\r\n", - options.headers, - ); - } - - let optsWithoutSignal = options; - if (optsWithoutSignal.signal) { - optsWithoutSignal = Object.assign({}, options); - delete optsWithoutSignal.signal; - } - - // initiate connection - if (this.agent) { - this.agent.addRequest(this, optsWithoutSignal); - } else { - // No agent, default to Connection:close. - this._last = true; - this.shouldKeepAlive = false; - if (typeof optsWithoutSignal.createConnection === "function") { - const oncreate = once((err, socket) => { - if (err) { - process.nextTick(() => this.emit("error", err)); - } else { - this.onSocket(socket); - } - }); - - try { - const newSocket = optsWithoutSignal.createConnection( - optsWithoutSignal, - oncreate, - ); - if (newSocket) { - oncreate(null, newSocket); - } - } catch (err) { - oncreate(err); - } - } else { - debug("CLIENT use net.createConnection", optsWithoutSignal); - this.onSocket(net.createConnection(optsWithoutSignal)); - } - } -} -Object.setPrototypeOf(ClientRequest.prototype, OutgoingMessage.prototype); -Object.setPrototypeOf(ClientRequest, OutgoingMessage); - -ClientRequest.prototype._finish = function _finish() { - DTRACE_HTTP_CLIENT_REQUEST(this, this.socket); - OutgoingMessage.prototype._finish.call(this); -}; - -ClientRequest.prototype._implicitHeader = function _implicitHeader() { - if (this._header) { - throw new ERR_HTTP_HEADERS_SENT("render"); - } - this._storeHeader( - this.method + " " + this.path + " HTTP/1.1\r\n", - this[kOutHeaders], - ); -}; - -ClientRequest.prototype.abort = function abort() { - if (this.aborted) { - return; - } - this.aborted = true; - process.nextTick(emitAbortNT, this); - this.destroy(); -}; - -ClientRequest.prototype.destroy = function destroy(err) { - if (this.destroyed) { - return this; - } - this.destroyed = true; - - // If we're aborting, we don't care about any more response data. - if (this.res) { - this.res._dump?.(); - } - - this[kError] = err; - this.socket?.destroy(err); - - return this; -}; - -function emitAbortNT(req) { - req.emit("abort"); -} - -function ondrain() { - const msg = this._httpMessage; - if (msg && !msg.finished && msg[kNeedDrain]) { - msg[kNeedDrain] = false; - msg.emit("drain"); - } -} - -function socketCloseListener() { - // deno-lint-ignore no-this-alias - const socket = this; - const req = socket._httpMessage; - debug("HTTP socket close"); - - // NOTE: It's important to get parser here, because it could be freed by - // the `socketOnData`. - const parser = socket.parser; - const res = req.res; - - req.destroyed = true; - if (res) { - // Socket closed before we emitted 'end' below. - if (!res.complete) { - res.destroy(connResetException("aborted")); - } - req._closed = true; - req.emit("close"); - if (!res.aborted && res.readable) { - res.push(null); - } - } else { - if (!req.socket._hadError) { - // This socket error fired before we started to - // receive a response. The error needs to - // fire on the request. - req.socket._hadError = true; - req.emit("error", connResetException("socket hang up")); - } - req._closed = true; - req.emit("close"); - } - - // Too bad. That output wasn't getting written. - // This is pretty terrible that it doesn't raise an error. - // Fixed better in v0.10 - if (req.outputData) { - req.outputData.length = 0; - } - - if (parser) { - parser.finish(); - freeParser(parser, req, socket); - } -} - -function socketErrorListener(err) { - // deno-lint-ignore no-this-alias - const socket = this; - const req = socket._httpMessage; - debug("SOCKET ERROR:", err.message, err.stack); - - if (req) { - // For Safety. Some additional errors might fire later on - // and we need to make sure we don't double-fire the error event. - req.socket._hadError = true; - req.emit("error", err); - } - - const parser = socket.parser; - if (parser) { - parser.finish(); - freeParser(parser, req, socket); - } - - // Ensure that no further data will come out of the socket - socket.removeListener("data", socketOnData); - socket.removeListener("end", socketOnEnd); - socket.destroy(); -} - -function socketOnEnd() { - // deno-lint-ignore no-this-alias - const socket = this; - const req = this._httpMessage; - const parser = this.parser; - - if (!req.res && !req.socket._hadError) { - // If we don't have a response then we know that the socket - // ended prematurely and we need to emit an error on the request. - req.socket._hadError = true; - req.emit("error", connResetException("socket hang up")); - } - if (parser) { - parser.finish(); - freeParser(parser, req, socket); - } - socket.destroy(); -} - -function socketOnData(d) { - // deno-lint-ignore no-this-alias - const socket = this; - const req = this._httpMessage; - const parser = this.parser; - - assert(parser && parser.socket === socket); - - const ret = parser.execute(d); - if (ret instanceof Error) { - prepareError(ret, parser, d); - debug("parse error", ret); - freeParser(parser, req, socket); - socket.removeListener("data", socketOnData); - socket.removeListener("end", socketOnEnd); - socket.destroy(); - req.socket._hadError = true; - req.emit("error", ret); - } else if (parser.incoming && parser.incoming.upgrade) { - // Upgrade (if status code 101) or CONNECT - const bytesParsed = ret; - const res = parser.incoming; - req.res = res; - - socket.removeListener("data", socketOnData); - socket.removeListener("end", socketOnEnd); - socket.removeListener("drain", ondrain); - - if (req.timeoutCb) socket.removeListener("timeout", req.timeoutCb); - socket.removeListener("timeout", responseOnTimeout); - - parser.finish(); - freeParser(parser, req, socket); - - const bodyHead = d.slice(bytesParsed, d.length); - - const eventName = req.method === "CONNECT" ? "connect" : "upgrade"; - if (req.listenerCount(eventName) > 0) { - req.upgradeOrConnect = true; - - // detach the socket - socket.emit("agentRemove"); - socket.removeListener("close", socketCloseListener); - socket.removeListener("error", socketErrorListener); - - socket._httpMessage = null; - socket.readableFlowing = null; - - req.emit(eventName, res, socket, bodyHead); - req.destroyed = true; - req._closed = true; - req.emit("close"); - } else { - // Requested Upgrade or used CONNECT method, but have no handler. - socket.destroy(); - } - } else if ( - parser.incoming && parser.incoming.complete && - // When the status code is informational (100, 102-199), - // the server will send a final response after this client - // sends a request body, so we must not free the parser. - // 101 (Switching Protocols) and all other status codes - // should be processed normally. - !statusIsInformational(parser.incoming.statusCode) - ) { - socket.removeListener("data", socketOnData); - socket.removeListener("end", socketOnEnd); - socket.removeListener("drain", ondrain); - freeParser(parser, req, socket); - } -} - -function statusIsInformational(status) { - // 100 (Continue) RFC7231 Section 6.2.1 - // 102 (Processing) RFC2518 - // 103 (Early Hints) RFC8297 - // 104-199 (Unassigned) - return (status < 200 && status >= 100 && status !== 101); -} - -// client -function parserOnIncomingClient(res, shouldKeepAlive) { - const socket = this.socket; - const req = socket._httpMessage; - - debug("AGENT incoming response!"); - - if (req.res) { - // We already have a response object, this means the server - // sent a double response. - socket.destroy(); - return 0; // No special treatment. - } - req.res = res; - - // Skip body and treat as Upgrade. - if (res.upgrade) { - return 2; - } - - // Responses to CONNECT request is handled as Upgrade. - const method = req.method; - if (method === "CONNECT") { - res.upgrade = true; - return 2; // Skip body and treat as Upgrade. - } - - if (statusIsInformational(res.statusCode)) { - // Restart the parser, as this is a 1xx informational message. - req.res = null; // Clear res so that we don't hit double-responses. - // Maintain compatibility by sending 100-specific events - if (res.statusCode === 100) { - req.emit("continue"); - } - // Send information events to all 1xx responses except 101 Upgrade. - req.emit("information", { - statusCode: res.statusCode, - statusMessage: res.statusMessage, - httpVersion: res.httpVersion, - httpVersionMajor: res.httpVersionMajor, - httpVersionMinor: res.httpVersionMinor, - headers: res.headers, - rawHeaders: res.rawHeaders, - }); - - return 1; // Skip body but don't treat as Upgrade. - } - - if (req.shouldKeepAlive && !shouldKeepAlive && !req.upgradeOrConnect) { - // Server MUST respond with Connection:keep-alive for us to enable it. - // If we've been upgraded (via WebSockets) we also shouldn't try to - // keep the connection open. - req.shouldKeepAlive = false; - } - - DTRACE_HTTP_CLIENT_RESPONSE(socket, req); - req.res = res; - res.req = req; - - // Add our listener first, so that we guarantee socket cleanup - res.on("end", responseOnEnd); - req.on("prefinish", requestOnPrefinish); - socket.on("timeout", responseOnTimeout); - - // If the user did not listen for the 'response' event, then they - // can't possibly read the data, so we ._dump() it into the void - // so that the socket doesn't hang there in a paused state. - if (req.aborted || !req.emit("response", res)) { - res._dump(); - } - - if (method === "HEAD") { - return 1; // Skip body but don't treat as Upgrade. - } - - if (res.statusCode === 304) { - res.complete = true; - return 1; // Skip body as there won't be any - } - - return 0; // No special treatment. -} - -// client -function responseKeepAlive(req) { - const socket = req.socket; - - debug("AGENT socket keep-alive"); - if (req.timeoutCb) { - socket.setTimeout(0, req.timeoutCb); - req.timeoutCb = null; - } - socket.removeListener("close", socketCloseListener); - socket.removeListener("error", socketErrorListener); - socket.removeListener("data", socketOnData); - socket.removeListener("end", socketOnEnd); - - // TODO(ronag): Between here and emitFreeNT the socket - // has no 'error' handler. - - // There are cases where _handle === null. Avoid those. Passing undefined to - // nextTick() will call getDefaultTriggerAsyncId() to retrieve the id. - const asyncId = socket._handle ? socket._handle.getAsyncId() : undefined; - // Mark this socket as available, AFTER user-added end - // handlers have a chance to run. - defaultTriggerAsyncIdScope(asyncId, process.nextTick, emitFreeNT, req); - - req.destroyed = true; - if (req.res) { - // Detach socket from IncomingMessage to avoid destroying the freed - // socket in IncomingMessage.destroy(). - req.res.socket = null; - } -} - -function responseOnEnd() { - const req = this.req; - const socket = req.socket; - - if (socket) { - if (req.timeoutCb) socket.removeListener("timeout", emitRequestTimeout); - socket.removeListener("timeout", responseOnTimeout); - } - - req._ended = true; - - if (!req.shouldKeepAlive) { - if (socket.writable) { - debug("AGENT socket.destroySoon()"); - if (typeof socket.destroySoon === "function") { - socket.destroySoon(); - } else { - socket.end(); - } - } - assert(!socket.writable); - } else if (req.finished && !this.aborted) { - // We can assume `req.finished` means all data has been written since: - // - `'responseOnEnd'` means we have been assigned a socket. - // - when we have a socket we write directly to it without buffering. - // - `req.finished` means `end()` has been called and no further data. - // can be written - responseKeepAlive(req); - } -} - -function responseOnTimeout() { - const req = this._httpMessage; - if (!req) return; - const res = req.res; - if (!res) return; - res.emit("timeout"); -} - -function requestOnPrefinish() { - // deno-lint-ignore no-this-alias - const req = this; - - if (req.shouldKeepAlive && req._ended) { - responseKeepAlive(req); - } -} - -function emitFreeNT(req) { - req._closed = true; - req.emit("close"); - if (req.socket) { - req.socket.emit("free"); - } -} - -function tickOnSocket(req, socket) { - const parser = parsers.alloc(); - req.socket = socket; - const lenient = req.insecureHTTPParser === undefined - ? isLenient() - : req.insecureHTTPParser; - parser.initialize( - HTTPParser.RESPONSE, - new HTTPClientAsyncResource("HTTPINCOMINGMESSAGE", req), - req.maxHeaderSize || 0, - lenient ? kLenientAll : kLenientNone, - 0, - ); - parser.socket = socket; - parser.outgoing = req; - req.parser = parser; - - socket.parser = parser; - socket._httpMessage = req; - - // Propagate headers limit from request object to parser - if (typeof req.maxHeadersCount === "number") { - parser.maxHeaderPairs = req.maxHeadersCount << 1; - } - - parser.onIncoming = parserOnIncomingClient; - socket.on("error", socketErrorListener); - socket.on("data", socketOnData); - socket.on("end", socketOnEnd); - socket.on("close", socketCloseListener); - socket.on("drain", ondrain); - - if ( - req.timeout !== undefined || - (req.agent && req.agent.options && req.agent.options.timeout) - ) { - listenSocketTimeout(req); - } - req.emit("socket", socket); -} - -function emitRequestTimeout() { - const req = this._httpMessage; - if (req) { - req.emit("timeout"); - } -} - -function listenSocketTimeout(req) { - if (req.timeoutCb) { - return; - } - // Set timeoutCb so it will get cleaned up on request end. - req.timeoutCb = emitRequestTimeout; - // Delegate socket timeout event. - if (req.socket) { - req.socket.once("timeout", emitRequestTimeout); - } else { - req.on("socket", (socket) => { - socket.once("timeout", emitRequestTimeout); - }); - } -} - -ClientRequest.prototype.onSocket = function onSocket(socket, err) { - // TODO(ronag): Between here and onSocketNT the socket - // has no 'error' handler. - process.nextTick(onSocketNT, this, socket, err); -}; - -function onSocketNT(req, socket, err) { - if (req.destroyed || err) { - req.destroyed = true; - - // deno-lint-ignore no-inner-declarations - function _destroy(req, err) { - if (!req.aborted && !err) { - err = connResetException("socket hang up"); - } - if (err) { - req.emit("error", err); - } - req._closed = true; - req.emit("close"); - } - - if (socket) { - if (!err && req.agent && !socket.destroyed) { - socket.emit("free"); - } else { - finished(socket.destroy(err || req[kError]), (er) => { - if (er?.code === "ERR_STREAM_PREMATURE_CLOSE") { - er = null; - } - _destroy(req, er || err); - }); - return; - } - } - - _destroy(req, err || req[kError]); - } else { - tickOnSocket(req, socket); - req._flush(); - } -} - -ClientRequest.prototype._deferToConnect = _deferToConnect; -function _deferToConnect(method, arguments_) { - // This function is for calls that need to happen once the socket is - // assigned to this request and writable. It's an important promisy - // thing for all the socket calls that happen either now - // (when a socket is assigned) or in the future (when a socket gets - // assigned out of the pool and is eventually writable). - - const callSocketMethod = () => { - if (method) { - Reflect.apply(this.socket[method], this.socket, arguments_); - } - }; - - const onSocket = () => { - if (this.socket.writable) { - callSocketMethod(); - } else { - this.socket.once("connect", callSocketMethod); - } - }; - - if (!this.socket) { - this.once("socket", onSocket); - } else { - onSocket(); - } -} - -ClientRequest.prototype.setTimeout = function setTimeout(msecs, callback) { - if (this._ended) { - return this; - } - - listenSocketTimeout(this); - msecs = getTimerDuration(msecs, "msecs"); - if (callback) this.once("timeout", callback); - - if (this.socket) { - setSocketTimeout(this.socket, msecs); - } else { - this.once("socket", (sock) => setSocketTimeout(sock, msecs)); - } - - return this; -}; - -function setSocketTimeout(sock, msecs) { - if (sock.connecting) { - sock.once("connect", function () { - sock.setTimeout(msecs); - }); - } else { - sock.setTimeout(msecs); - } -} - -ClientRequest.prototype.setNoDelay = function setNoDelay(noDelay) { - this._deferToConnect("setNoDelay", [noDelay]); -}; - -ClientRequest.prototype.setSocketKeepAlive = function setSocketKeepAlive( - enable, - initialDelay, -) { - this._deferToConnect("setKeepAlive", [enable, initialDelay]); -}; - -ClientRequest.prototype.clearTimeout = function clearTimeout(cb) { - this.setTimeout(0, cb); -}; - -export default { - ClientRequest, -}; From bb984480c95186f650955e72a2ccd5e2b96beaa9 Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Wed, 19 Jan 2022 13:39:18 +0900 Subject: [PATCH 9/9] chore: rerun ci to fix CLA status