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