11cb0ef41Sopenharmony_ci'use strict'; 21cb0ef41Sopenharmony_ci 31cb0ef41Sopenharmony_ciconst common = require('../common'); 41cb0ef41Sopenharmony_ciconst fixtures = require('../common/fixtures'); 51cb0ef41Sopenharmony_ciif (!common.hasCrypto) 61cb0ef41Sopenharmony_ci common.skip('missing crypto'); 71cb0ef41Sopenharmony_ciconst assert = require('assert'); 81cb0ef41Sopenharmony_ciconst net = require('net'); 91cb0ef41Sopenharmony_ciconst tls = require('tls'); 101cb0ef41Sopenharmony_ciconst h2 = require('http2'); 111cb0ef41Sopenharmony_ci 121cb0ef41Sopenharmony_ci// This test sets up an H2 proxy server, and tunnels a request over one of its streams 131cb0ef41Sopenharmony_ci// back to itself, via TLS, and then closes the TLS connection. On some Node versions 141cb0ef41Sopenharmony_ci// (v18 & v20 up to 20.5.1) the resulting JS Stream Socket fails to shutdown correctly 151cb0ef41Sopenharmony_ci// in this case, and crashes due to a null pointer in finishShutdown. 161cb0ef41Sopenharmony_ci 171cb0ef41Sopenharmony_ciconst tlsOptions = { 181cb0ef41Sopenharmony_ci key: fixtures.readKey('agent1-key.pem'), 191cb0ef41Sopenharmony_ci cert: fixtures.readKey('agent1-cert.pem'), 201cb0ef41Sopenharmony_ci ALPNProtocols: ['h2'] 211cb0ef41Sopenharmony_ci}; 221cb0ef41Sopenharmony_ci 231cb0ef41Sopenharmony_ciconst netServer = net.createServer((socket) => { 241cb0ef41Sopenharmony_ci socket.allowHalfOpen = false; 251cb0ef41Sopenharmony_ci // ^ This allows us to trigger this reliably, but it's not strictly required 261cb0ef41Sopenharmony_ci // for the bug and crash to happen, skipping this just fails elsewhere later. 271cb0ef41Sopenharmony_ci 281cb0ef41Sopenharmony_ci h2Server.emit('connection', socket); 291cb0ef41Sopenharmony_ci}); 301cb0ef41Sopenharmony_ci 311cb0ef41Sopenharmony_ciconst h2Server = h2.createSecureServer(tlsOptions, (req, res) => { 321cb0ef41Sopenharmony_ci res.writeHead(200); 331cb0ef41Sopenharmony_ci res.end(); 341cb0ef41Sopenharmony_ci}); 351cb0ef41Sopenharmony_ci 361cb0ef41Sopenharmony_cih2Server.on('connect', (req, res) => { 371cb0ef41Sopenharmony_ci res.writeHead(200, {}); 381cb0ef41Sopenharmony_ci netServer.emit('connection', res.stream); 391cb0ef41Sopenharmony_ci}); 401cb0ef41Sopenharmony_ci 411cb0ef41Sopenharmony_cinetServer.listen(0, common.mustCall(() => { 421cb0ef41Sopenharmony_ci const proxyClient = h2.connect(`https://localhost:${netServer.address().port}`, { 431cb0ef41Sopenharmony_ci rejectUnauthorized: false 441cb0ef41Sopenharmony_ci }); 451cb0ef41Sopenharmony_ci 461cb0ef41Sopenharmony_ci const proxyReq = proxyClient.request({ 471cb0ef41Sopenharmony_ci ':method': 'CONNECT', 481cb0ef41Sopenharmony_ci ':authority': 'example.com:443' 491cb0ef41Sopenharmony_ci }); 501cb0ef41Sopenharmony_ci 511cb0ef41Sopenharmony_ci proxyReq.on('response', common.mustCall((response) => { 521cb0ef41Sopenharmony_ci assert.strictEqual(response[':status'], 200); 531cb0ef41Sopenharmony_ci 541cb0ef41Sopenharmony_ci // Create a TLS socket within the tunnel, and start sending a request: 551cb0ef41Sopenharmony_ci const tlsSocket = tls.connect({ 561cb0ef41Sopenharmony_ci socket: proxyReq, 571cb0ef41Sopenharmony_ci ALPNProtocols: ['h2'], 581cb0ef41Sopenharmony_ci rejectUnauthorized: false 591cb0ef41Sopenharmony_ci }); 601cb0ef41Sopenharmony_ci 611cb0ef41Sopenharmony_ci proxyReq.on('close', common.mustCall(() => { 621cb0ef41Sopenharmony_ci proxyClient.close(); 631cb0ef41Sopenharmony_ci netServer.close(); 641cb0ef41Sopenharmony_ci })); 651cb0ef41Sopenharmony_ci 661cb0ef41Sopenharmony_ci // Forcibly kill the TLS socket 671cb0ef41Sopenharmony_ci tlsSocket.destroy(); 681cb0ef41Sopenharmony_ci 691cb0ef41Sopenharmony_ci // This results in an async error in affected Node versions, before the 'close' event 701cb0ef41Sopenharmony_ci })); 711cb0ef41Sopenharmony_ci})); 72