11cb0ef41Sopenharmony_ci// Flags: --expose-internals 21cb0ef41Sopenharmony_ci 31cb0ef41Sopenharmony_ci'use strict'; 41cb0ef41Sopenharmony_ci 51cb0ef41Sopenharmony_ciconst common = require('../common'); 61cb0ef41Sopenharmony_ciif (!common.hasCrypto) 71cb0ef41Sopenharmony_ci common.skip('missing crypto'); 81cb0ef41Sopenharmony_ciconst assert = require('assert'); 91cb0ef41Sopenharmony_ciconst h2 = require('http2'); 101cb0ef41Sopenharmony_ciconst { kSocket } = require('internal/http2/util'); 111cb0ef41Sopenharmony_ciconst Countdown = require('../common/countdown'); 121cb0ef41Sopenharmony_ciconst { getEventListeners } = require('events'); 131cb0ef41Sopenharmony_ci{ 141cb0ef41Sopenharmony_ci const server = h2.createServer(); 151cb0ef41Sopenharmony_ci server.listen(0, common.mustCall(() => { 161cb0ef41Sopenharmony_ci const destroyCallbacks = [ 171cb0ef41Sopenharmony_ci (client) => client.destroy(), 181cb0ef41Sopenharmony_ci (client) => client[kSocket].destroy(), 191cb0ef41Sopenharmony_ci ]; 201cb0ef41Sopenharmony_ci 211cb0ef41Sopenharmony_ci const countdown = new Countdown(destroyCallbacks.length, () => { 221cb0ef41Sopenharmony_ci server.close(); 231cb0ef41Sopenharmony_ci }); 241cb0ef41Sopenharmony_ci 251cb0ef41Sopenharmony_ci for (const destroyCallback of destroyCallbacks) { 261cb0ef41Sopenharmony_ci const client = h2.connect(`http://localhost:${server.address().port}`); 271cb0ef41Sopenharmony_ci client.on('connect', common.mustCall(() => { 281cb0ef41Sopenharmony_ci const socket = client[kSocket]; 291cb0ef41Sopenharmony_ci 301cb0ef41Sopenharmony_ci assert(socket, 'client session has associated socket'); 311cb0ef41Sopenharmony_ci assert( 321cb0ef41Sopenharmony_ci !client.destroyed, 331cb0ef41Sopenharmony_ci 'client has not been destroyed before destroy is called' 341cb0ef41Sopenharmony_ci ); 351cb0ef41Sopenharmony_ci assert( 361cb0ef41Sopenharmony_ci !socket.destroyed, 371cb0ef41Sopenharmony_ci 'socket has not been destroyed before destroy is called' 381cb0ef41Sopenharmony_ci ); 391cb0ef41Sopenharmony_ci 401cb0ef41Sopenharmony_ci destroyCallback(client); 411cb0ef41Sopenharmony_ci 421cb0ef41Sopenharmony_ci client.on('close', common.mustCall(() => { 431cb0ef41Sopenharmony_ci assert(client.destroyed); 441cb0ef41Sopenharmony_ci })); 451cb0ef41Sopenharmony_ci 461cb0ef41Sopenharmony_ci countdown.dec(); 471cb0ef41Sopenharmony_ci })); 481cb0ef41Sopenharmony_ci } 491cb0ef41Sopenharmony_ci })); 501cb0ef41Sopenharmony_ci} 511cb0ef41Sopenharmony_ci 521cb0ef41Sopenharmony_ci// Test destroy before client operations 531cb0ef41Sopenharmony_ci{ 541cb0ef41Sopenharmony_ci const server = h2.createServer(); 551cb0ef41Sopenharmony_ci server.listen(0, common.mustCall(() => { 561cb0ef41Sopenharmony_ci const client = h2.connect(`http://localhost:${server.address().port}`); 571cb0ef41Sopenharmony_ci const socket = client[kSocket]; 581cb0ef41Sopenharmony_ci socket.on('close', common.mustCall(() => { 591cb0ef41Sopenharmony_ci assert(socket.destroyed); 601cb0ef41Sopenharmony_ci })); 611cb0ef41Sopenharmony_ci 621cb0ef41Sopenharmony_ci const req = client.request(); 631cb0ef41Sopenharmony_ci req.on('error', common.expectsError({ 641cb0ef41Sopenharmony_ci code: 'ERR_HTTP2_STREAM_CANCEL', 651cb0ef41Sopenharmony_ci name: 'Error', 661cb0ef41Sopenharmony_ci message: 'The pending stream has been canceled' 671cb0ef41Sopenharmony_ci })); 681cb0ef41Sopenharmony_ci 691cb0ef41Sopenharmony_ci client.destroy(); 701cb0ef41Sopenharmony_ci 711cb0ef41Sopenharmony_ci req.on('response', common.mustNotCall()); 721cb0ef41Sopenharmony_ci 731cb0ef41Sopenharmony_ci const sessionError = { 741cb0ef41Sopenharmony_ci name: 'Error', 751cb0ef41Sopenharmony_ci code: 'ERR_HTTP2_INVALID_SESSION', 761cb0ef41Sopenharmony_ci message: 'The session has been destroyed' 771cb0ef41Sopenharmony_ci }; 781cb0ef41Sopenharmony_ci 791cb0ef41Sopenharmony_ci assert.throws(() => client.setNextStreamID(), sessionError); 801cb0ef41Sopenharmony_ci assert.throws(() => client.setLocalWindowSize(), sessionError); 811cb0ef41Sopenharmony_ci assert.throws(() => client.ping(), sessionError); 821cb0ef41Sopenharmony_ci assert.throws(() => client.settings({}), sessionError); 831cb0ef41Sopenharmony_ci assert.throws(() => client.goaway(), sessionError); 841cb0ef41Sopenharmony_ci assert.throws(() => client.request(), sessionError); 851cb0ef41Sopenharmony_ci client.close(); // Should be a non-op at this point 861cb0ef41Sopenharmony_ci 871cb0ef41Sopenharmony_ci // Wait for setImmediate call from destroy() to complete 881cb0ef41Sopenharmony_ci // so that state.destroyed is set to true 891cb0ef41Sopenharmony_ci setImmediate(() => { 901cb0ef41Sopenharmony_ci assert.throws(() => client.setNextStreamID(), sessionError); 911cb0ef41Sopenharmony_ci assert.throws(() => client.setLocalWindowSize(), sessionError); 921cb0ef41Sopenharmony_ci assert.throws(() => client.ping(), sessionError); 931cb0ef41Sopenharmony_ci assert.throws(() => client.settings({}), sessionError); 941cb0ef41Sopenharmony_ci assert.throws(() => client.goaway(), sessionError); 951cb0ef41Sopenharmony_ci assert.throws(() => client.request(), sessionError); 961cb0ef41Sopenharmony_ci client.close(); // Should be a non-op at this point 971cb0ef41Sopenharmony_ci }); 981cb0ef41Sopenharmony_ci 991cb0ef41Sopenharmony_ci req.resume(); 1001cb0ef41Sopenharmony_ci req.on('end', common.mustNotCall()); 1011cb0ef41Sopenharmony_ci req.on('close', common.mustCall(() => server.close())); 1021cb0ef41Sopenharmony_ci })); 1031cb0ef41Sopenharmony_ci} 1041cb0ef41Sopenharmony_ci 1051cb0ef41Sopenharmony_ci// Test destroy before goaway 1061cb0ef41Sopenharmony_ci{ 1071cb0ef41Sopenharmony_ci const server = h2.createServer(); 1081cb0ef41Sopenharmony_ci server.on('stream', common.mustCall((stream) => { 1091cb0ef41Sopenharmony_ci stream.session.destroy(); 1101cb0ef41Sopenharmony_ci })); 1111cb0ef41Sopenharmony_ci 1121cb0ef41Sopenharmony_ci server.listen(0, common.mustCall(() => { 1131cb0ef41Sopenharmony_ci const client = h2.connect(`http://localhost:${server.address().port}`); 1141cb0ef41Sopenharmony_ci 1151cb0ef41Sopenharmony_ci client.on('close', () => { 1161cb0ef41Sopenharmony_ci server.close(); 1171cb0ef41Sopenharmony_ci // Calling destroy in here should not matter 1181cb0ef41Sopenharmony_ci client.destroy(); 1191cb0ef41Sopenharmony_ci }); 1201cb0ef41Sopenharmony_ci 1211cb0ef41Sopenharmony_ci client.request(); 1221cb0ef41Sopenharmony_ci })); 1231cb0ef41Sopenharmony_ci} 1241cb0ef41Sopenharmony_ci 1251cb0ef41Sopenharmony_ci// Test destroy before connect 1261cb0ef41Sopenharmony_ci{ 1271cb0ef41Sopenharmony_ci const server = h2.createServer(); 1281cb0ef41Sopenharmony_ci server.on('stream', common.mustNotCall()); 1291cb0ef41Sopenharmony_ci 1301cb0ef41Sopenharmony_ci server.listen(0, common.mustCall(() => { 1311cb0ef41Sopenharmony_ci const client = h2.connect(`http://localhost:${server.address().port}`); 1321cb0ef41Sopenharmony_ci 1331cb0ef41Sopenharmony_ci server.on('connection', common.mustCall(() => { 1341cb0ef41Sopenharmony_ci server.close(); 1351cb0ef41Sopenharmony_ci client.close(); 1361cb0ef41Sopenharmony_ci })); 1371cb0ef41Sopenharmony_ci 1381cb0ef41Sopenharmony_ci const req = client.request(); 1391cb0ef41Sopenharmony_ci req.destroy(); 1401cb0ef41Sopenharmony_ci })); 1411cb0ef41Sopenharmony_ci} 1421cb0ef41Sopenharmony_ci 1431cb0ef41Sopenharmony_ci// Test close before connect 1441cb0ef41Sopenharmony_ci{ 1451cb0ef41Sopenharmony_ci const server = h2.createServer(); 1461cb0ef41Sopenharmony_ci 1471cb0ef41Sopenharmony_ci server.on('stream', common.mustNotCall()); 1481cb0ef41Sopenharmony_ci server.listen(0, common.mustCall(() => { 1491cb0ef41Sopenharmony_ci const client = h2.connect(`http://localhost:${server.address().port}`); 1501cb0ef41Sopenharmony_ci client.on('close', common.mustCall()); 1511cb0ef41Sopenharmony_ci const socket = client[kSocket]; 1521cb0ef41Sopenharmony_ci socket.on('close', common.mustCall(() => { 1531cb0ef41Sopenharmony_ci assert(socket.destroyed); 1541cb0ef41Sopenharmony_ci })); 1551cb0ef41Sopenharmony_ci 1561cb0ef41Sopenharmony_ci const req = client.request(); 1571cb0ef41Sopenharmony_ci // Should throw goaway error 1581cb0ef41Sopenharmony_ci req.on('error', common.expectsError({ 1591cb0ef41Sopenharmony_ci code: 'ERR_HTTP2_GOAWAY_SESSION', 1601cb0ef41Sopenharmony_ci name: 'Error', 1611cb0ef41Sopenharmony_ci message: 'New streams cannot be created after receiving a GOAWAY' 1621cb0ef41Sopenharmony_ci })); 1631cb0ef41Sopenharmony_ci 1641cb0ef41Sopenharmony_ci client.close(); 1651cb0ef41Sopenharmony_ci req.resume(); 1661cb0ef41Sopenharmony_ci req.on('end', common.mustNotCall()); 1671cb0ef41Sopenharmony_ci req.on('close', common.mustCall(() => server.close())); 1681cb0ef41Sopenharmony_ci })); 1691cb0ef41Sopenharmony_ci} 1701cb0ef41Sopenharmony_ci 1711cb0ef41Sopenharmony_ci// Destroy with AbortSignal 1721cb0ef41Sopenharmony_ci{ 1731cb0ef41Sopenharmony_ci const server = h2.createServer(); 1741cb0ef41Sopenharmony_ci const controller = new AbortController(); 1751cb0ef41Sopenharmony_ci 1761cb0ef41Sopenharmony_ci server.on('stream', common.mustNotCall()); 1771cb0ef41Sopenharmony_ci server.listen(0, common.mustCall(() => { 1781cb0ef41Sopenharmony_ci const client = h2.connect(`http://localhost:${server.address().port}`); 1791cb0ef41Sopenharmony_ci client.on('close', common.mustCall()); 1801cb0ef41Sopenharmony_ci 1811cb0ef41Sopenharmony_ci const { signal } = controller; 1821cb0ef41Sopenharmony_ci assert.strictEqual(getEventListeners(signal, 'abort').length, 0); 1831cb0ef41Sopenharmony_ci 1841cb0ef41Sopenharmony_ci client.on('error', common.mustCall(() => { 1851cb0ef41Sopenharmony_ci // After underlying stream dies, signal listener detached 1861cb0ef41Sopenharmony_ci assert.strictEqual(getEventListeners(signal, 'abort').length, 0); 1871cb0ef41Sopenharmony_ci })); 1881cb0ef41Sopenharmony_ci 1891cb0ef41Sopenharmony_ci const req = client.request({}, { signal }); 1901cb0ef41Sopenharmony_ci 1911cb0ef41Sopenharmony_ci req.on('error', common.mustCall((err) => { 1921cb0ef41Sopenharmony_ci assert.strictEqual(err.code, 'ABORT_ERR'); 1931cb0ef41Sopenharmony_ci assert.strictEqual(err.name, 'AbortError'); 1941cb0ef41Sopenharmony_ci })); 1951cb0ef41Sopenharmony_ci req.on('close', common.mustCall(() => server.close())); 1961cb0ef41Sopenharmony_ci 1971cb0ef41Sopenharmony_ci assert.strictEqual(req.aborted, false); 1981cb0ef41Sopenharmony_ci assert.strictEqual(req.destroyed, false); 1991cb0ef41Sopenharmony_ci // Signal listener attached 2001cb0ef41Sopenharmony_ci assert.strictEqual(getEventListeners(signal, 'abort').length, 1); 2011cb0ef41Sopenharmony_ci 2021cb0ef41Sopenharmony_ci controller.abort(); 2031cb0ef41Sopenharmony_ci 2041cb0ef41Sopenharmony_ci assert.strictEqual(req.aborted, false); 2051cb0ef41Sopenharmony_ci assert.strictEqual(req.destroyed, true); 2061cb0ef41Sopenharmony_ci })); 2071cb0ef41Sopenharmony_ci} 2081cb0ef41Sopenharmony_ci// Pass an already destroyed signal to abort immediately. 2091cb0ef41Sopenharmony_ci{ 2101cb0ef41Sopenharmony_ci const server = h2.createServer(); 2111cb0ef41Sopenharmony_ci const controller = new AbortController(); 2121cb0ef41Sopenharmony_ci 2131cb0ef41Sopenharmony_ci server.on('stream', common.mustNotCall()); 2141cb0ef41Sopenharmony_ci server.listen(0, common.mustCall(() => { 2151cb0ef41Sopenharmony_ci const client = h2.connect(`http://localhost:${server.address().port}`); 2161cb0ef41Sopenharmony_ci client.on('close', common.mustCall()); 2171cb0ef41Sopenharmony_ci 2181cb0ef41Sopenharmony_ci const { signal } = controller; 2191cb0ef41Sopenharmony_ci controller.abort(); 2201cb0ef41Sopenharmony_ci 2211cb0ef41Sopenharmony_ci assert.strictEqual(getEventListeners(signal, 'abort').length, 0); 2221cb0ef41Sopenharmony_ci 2231cb0ef41Sopenharmony_ci client.on('error', common.mustCall(() => { 2241cb0ef41Sopenharmony_ci // After underlying stream dies, signal listener detached 2251cb0ef41Sopenharmony_ci assert.strictEqual(getEventListeners(signal, 'abort').length, 0); 2261cb0ef41Sopenharmony_ci })); 2271cb0ef41Sopenharmony_ci 2281cb0ef41Sopenharmony_ci const req = client.request({}, { signal }); 2291cb0ef41Sopenharmony_ci // Signal already aborted, so no event listener attached. 2301cb0ef41Sopenharmony_ci assert.strictEqual(getEventListeners(signal, 'abort').length, 0); 2311cb0ef41Sopenharmony_ci 2321cb0ef41Sopenharmony_ci assert.strictEqual(req.aborted, false); 2331cb0ef41Sopenharmony_ci // Destroyed on same tick as request made 2341cb0ef41Sopenharmony_ci assert.strictEqual(req.destroyed, true); 2351cb0ef41Sopenharmony_ci 2361cb0ef41Sopenharmony_ci req.on('error', common.mustCall((err) => { 2371cb0ef41Sopenharmony_ci assert.strictEqual(err.code, 'ABORT_ERR'); 2381cb0ef41Sopenharmony_ci assert.strictEqual(err.name, 'AbortError'); 2391cb0ef41Sopenharmony_ci })); 2401cb0ef41Sopenharmony_ci req.on('close', common.mustCall(() => server.close())); 2411cb0ef41Sopenharmony_ci })); 2421cb0ef41Sopenharmony_ci} 2431cb0ef41Sopenharmony_ci 2441cb0ef41Sopenharmony_ci 2451cb0ef41Sopenharmony_ci// Destroy ClientHttpSession with AbortSignal 2461cb0ef41Sopenharmony_ci{ 2471cb0ef41Sopenharmony_ci function testH2ConnectAbort(secure) { 2481cb0ef41Sopenharmony_ci const server = secure ? h2.createSecureServer() : h2.createServer(); 2491cb0ef41Sopenharmony_ci const controller = new AbortController(); 2501cb0ef41Sopenharmony_ci 2511cb0ef41Sopenharmony_ci server.on('stream', common.mustNotCall()); 2521cb0ef41Sopenharmony_ci server.listen(0, common.mustCall(() => { 2531cb0ef41Sopenharmony_ci const { signal } = controller; 2541cb0ef41Sopenharmony_ci const protocol = secure ? 'https' : 'http'; 2551cb0ef41Sopenharmony_ci const client = h2.connect(`${protocol}://localhost:${server.address().port}`, { 2561cb0ef41Sopenharmony_ci signal, 2571cb0ef41Sopenharmony_ci }); 2581cb0ef41Sopenharmony_ci client.on('close', common.mustCall()); 2591cb0ef41Sopenharmony_ci assert.strictEqual(getEventListeners(signal, 'abort').length, 1); 2601cb0ef41Sopenharmony_ci 2611cb0ef41Sopenharmony_ci client.on('error', common.mustCall(common.mustCall((err) => { 2621cb0ef41Sopenharmony_ci assert.strictEqual(err.code, 'ABORT_ERR'); 2631cb0ef41Sopenharmony_ci assert.strictEqual(err.name, 'AbortError'); 2641cb0ef41Sopenharmony_ci }))); 2651cb0ef41Sopenharmony_ci 2661cb0ef41Sopenharmony_ci const req = client.request({}, {}); 2671cb0ef41Sopenharmony_ci assert.strictEqual(getEventListeners(signal, 'abort').length, 1); 2681cb0ef41Sopenharmony_ci 2691cb0ef41Sopenharmony_ci req.on('error', common.mustCall((err) => { 2701cb0ef41Sopenharmony_ci assert.strictEqual(err.code, 'ERR_HTTP2_STREAM_CANCEL'); 2711cb0ef41Sopenharmony_ci assert.strictEqual(err.name, 'Error'); 2721cb0ef41Sopenharmony_ci assert.strictEqual(req.aborted, false); 2731cb0ef41Sopenharmony_ci assert.strictEqual(req.destroyed, true); 2741cb0ef41Sopenharmony_ci })); 2751cb0ef41Sopenharmony_ci req.on('close', common.mustCall(() => server.close())); 2761cb0ef41Sopenharmony_ci 2771cb0ef41Sopenharmony_ci assert.strictEqual(req.aborted, false); 2781cb0ef41Sopenharmony_ci assert.strictEqual(req.destroyed, false); 2791cb0ef41Sopenharmony_ci // Signal listener attached 2801cb0ef41Sopenharmony_ci assert.strictEqual(getEventListeners(signal, 'abort').length, 1); 2811cb0ef41Sopenharmony_ci 2821cb0ef41Sopenharmony_ci controller.abort(); 2831cb0ef41Sopenharmony_ci })); 2841cb0ef41Sopenharmony_ci } 2851cb0ef41Sopenharmony_ci testH2ConnectAbort(false); 2861cb0ef41Sopenharmony_ci testH2ConnectAbort(true); 2871cb0ef41Sopenharmony_ci} 2881cb0ef41Sopenharmony_ci 2891cb0ef41Sopenharmony_ci// Destroy ClientHttp2Stream with AbortSignal 2901cb0ef41Sopenharmony_ci{ 2911cb0ef41Sopenharmony_ci const server = h2.createServer(); 2921cb0ef41Sopenharmony_ci const controller = new AbortController(); 2931cb0ef41Sopenharmony_ci 2941cb0ef41Sopenharmony_ci server.on('stream', common.mustCall((stream) => { 2951cb0ef41Sopenharmony_ci stream.on('error', common.mustNotCall()); 2961cb0ef41Sopenharmony_ci stream.on('close', common.mustCall(() => { 2971cb0ef41Sopenharmony_ci assert.strictEqual(stream.rstCode, h2.constants.NGHTTP2_CANCEL); 2981cb0ef41Sopenharmony_ci server.close(); 2991cb0ef41Sopenharmony_ci })); 3001cb0ef41Sopenharmony_ci controller.abort(); 3011cb0ef41Sopenharmony_ci })); 3021cb0ef41Sopenharmony_ci server.listen(0, common.mustCall(() => { 3031cb0ef41Sopenharmony_ci const client = h2.connect(`http://localhost:${server.address().port}`); 3041cb0ef41Sopenharmony_ci client.on('close', common.mustCall()); 3051cb0ef41Sopenharmony_ci 3061cb0ef41Sopenharmony_ci const { signal } = controller; 3071cb0ef41Sopenharmony_ci const req = client.request({}, { signal }); 3081cb0ef41Sopenharmony_ci assert.strictEqual(getEventListeners(signal, 'abort').length, 1); 3091cb0ef41Sopenharmony_ci req.on('error', common.mustCall((err) => { 3101cb0ef41Sopenharmony_ci assert.strictEqual(err.code, 'ABORT_ERR'); 3111cb0ef41Sopenharmony_ci assert.strictEqual(err.name, 'AbortError'); 3121cb0ef41Sopenharmony_ci client.close(); 3131cb0ef41Sopenharmony_ci })); 3141cb0ef41Sopenharmony_ci req.on('close', common.mustCall()); 3151cb0ef41Sopenharmony_ci })); 3161cb0ef41Sopenharmony_ci} 317