current commit
260902.10 · quic: reuse TLS pause machinery to drop event deferral & improve 0RTT
'use strict';

// Measures the cost of establishing QUIC sessions: how many complete
// handshakes per second a single endpoint can serve, for raw QUIC and for
// HTTP/3. Nothing is sent on the session beyond what the protocol itself
// requires, so this isolates connection setup rather than data transfer.

const common = require('../common.js');
const fixtures = require('../../test/common/fixtures');
const { createPrivateKey } = require('crypto');

const bench = common.createBenchmark(main, {
  // 'raw' negotiates a non-HTTP ALPN and does no application work.
  // 'h3' negotiates HTTP/3, so the server also builds an nghttp3 connection
  // and its control/QPACK streams for every session.
  protocol: ['raw', 'h3'],
  concurrency: [1, 10],
  n: [1000],
}, { flags: ['--experimental-quic', '--no-warnings'] });

async function main({ protocol, concurrency, n }) {
  const { listen, connect } = require('node:quic');

  const key = createPrivateKey(fixtures.readKey('agent1-key.pem'));
  const cert = fixtures.readKey('agent1-cert.pem');
  const alpn = protocol === 'h3' ? 'h3' : 'quic-bench';

  const endpoint = await listen((session) => {
    // A benchmark peer never reads these; swallow so a torn-down session
    // cannot produce an unhandled rejection.
    session.opened.catch(() => {});
    session.closed.catch(() => {});
  }, {
    sni: { '*': { keys: [key], certs: [cert] } },
    alpn: [alpn],
    // The defaults rate-limit session creation per host, which a benchmark
    // hammering a single address would otherwise trip.
    endpoint: {
      maxConnectionsPerHost: 0xFFFF,
      maxConnectionsTotal: 0xFFFF,
      sessionCreationRate: 1_000_000,
      sessionCreationBurst: 1_000_000,
    },
  });

  const address = endpoint.address;

  async function handshake() {
    const session = await connect(address, {
      servername: 'localhost',
      verifyPeer: 'manual',
      alpn,
    });
    await session.opened;
    session.close();
    await session.closed.catch(() => {});
  }

  async function run(count) {
    for (let i = 0; i < count; i += concurrency) {
      const batch = Math.min(concurrency, count - i);
      await Promise.all(Array.from({ length: batch }, handshake));
    }
  }

  // Warm up the TLS and QUIC machinery before measuring.
  await run(Math.min(100, n));

  bench.start();
  await run(n);
  bench.end(n);

  await endpoint.close();
}