1'use strict'; 2 3const common = require('../common'); 4const fixtures = require('../common/fixtures'); 5if (!common.hasCrypto) 6 common.skip('missing crypto'); 7const assert = require('assert'); 8const net = require('net'); 9const tls = require('tls'); 10const h2 = require('http2'); 11 12// This test sets up an H2 proxy server, and tunnels a request over one of its streams 13// back to itself, via TLS, and then closes the TLS connection. On some Node versions 14// (v18 & v20 up to 20.5.1) the resulting JS Stream Socket fails to shutdown correctly 15// in this case, and crashes due to a null pointer in finishShutdown. 16 17const tlsOptions = { 18 key: fixtures.readKey('agent1-key.pem'), 19 cert: fixtures.readKey('agent1-cert.pem'), 20 ALPNProtocols: ['h2'] 21}; 22 23const netServer = net.createServer((socket) => { 24 socket.allowHalfOpen = false; 25 // ^ This allows us to trigger this reliably, but it's not strictly required 26 // for the bug and crash to happen, skipping this just fails elsewhere later. 27 28 h2Server.emit('connection', socket); 29}); 30 31const h2Server = h2.createSecureServer(tlsOptions, (req, res) => { 32 res.writeHead(200); 33 res.end(); 34}); 35 36h2Server.on('connect', (req, res) => { 37 res.writeHead(200, {}); 38 netServer.emit('connection', res.stream); 39}); 40 41netServer.listen(0, common.mustCall(() => { 42 const proxyClient = h2.connect(`https://localhost:${netServer.address().port}`, { 43 rejectUnauthorized: false 44 }); 45 46 const proxyReq = proxyClient.request({ 47 ':method': 'CONNECT', 48 ':authority': 'example.com:443' 49 }); 50 51 proxyReq.on('response', common.mustCall((response) => { 52 assert.strictEqual(response[':status'], 200); 53 54 // Create a TLS socket within the tunnel, and start sending a request: 55 const tlsSocket = tls.connect({ 56 socket: proxyReq, 57 ALPNProtocols: ['h2'], 58 rejectUnauthorized: false 59 }); 60 61 proxyReq.on('close', common.mustCall(() => { 62 proxyClient.close(); 63 netServer.close(); 64 })); 65 66 // Forcibly kill the TLS socket 67 tlsSocket.destroy(); 68 69 // This results in an async error in affected Node versions, before the 'close' event 70 })); 71})); 72