1'use strict'; 2 3const common = require('../common'); 4if (!common.hasCrypto) 5 common.skip('missing crypto'); 6const assert = require('assert'); 7const net = require('net'); 8const http2 = require('http2'); 9const { URL } = require('url'); 10 11const { 12 HTTP2_HEADER_METHOD, 13 HTTP2_HEADER_AUTHORITY, 14 HTTP2_HEADER_SCHEME, 15 HTTP2_HEADER_PATH, 16 NGHTTP2_CONNECT_ERROR, 17 NGHTTP2_REFUSED_STREAM 18} = http2.constants; 19 20const server = net.createServer(common.mustCall((socket) => { 21 let data = ''; 22 socket.setEncoding('utf8'); 23 socket.on('data', (chunk) => data += chunk); 24 socket.on('end', common.mustCall(() => { 25 assert.strictEqual(data, 'hello'); 26 })); 27 socket.on('close', common.mustCall()); 28 socket.end('hello'); 29})); 30 31server.listen(0, common.mustCall(() => { 32 33 const port = server.address().port; 34 35 const proxy = http2.createServer(); 36 proxy.on('stream', common.mustCall((stream, headers) => { 37 if (headers[HTTP2_HEADER_METHOD] !== 'CONNECT') { 38 stream.close(NGHTTP2_REFUSED_STREAM); 39 return; 40 } 41 const auth = new URL(`tcp://${headers[HTTP2_HEADER_AUTHORITY]}`); 42 assert.strictEqual(auth.hostname, 'localhost'); 43 assert.strictEqual(+auth.port, port); 44 const socket = net.connect(auth.port, auth.hostname, () => { 45 stream.respond(); 46 socket.pipe(stream); 47 stream.pipe(socket); 48 }); 49 socket.on('close', common.mustCall()); 50 socket.on('error', (error) => { 51 stream.close(NGHTTP2_CONNECT_ERROR); 52 }); 53 })); 54 55 proxy.listen(0, () => { 56 const client = http2.connect(`http://localhost:${proxy.address().port}`); 57 58 // Confirm that :authority is required and :scheme & :path are forbidden 59 assert.throws( 60 () => client.request({ 61 [HTTP2_HEADER_METHOD]: 'CONNECT' 62 }), 63 { 64 code: 'ERR_HTTP2_CONNECT_AUTHORITY', 65 message: ':authority header is required for CONNECT requests' 66 } 67 ); 68 assert.throws( 69 () => client.request({ 70 [HTTP2_HEADER_METHOD]: 'CONNECT', 71 [HTTP2_HEADER_AUTHORITY]: `localhost:${port}`, 72 [HTTP2_HEADER_SCHEME]: 'http' 73 }), 74 { 75 code: 'ERR_HTTP2_CONNECT_SCHEME', 76 message: 'The :scheme header is forbidden for CONNECT requests' 77 } 78 ); 79 assert.throws( 80 () => client.request({ 81 [HTTP2_HEADER_METHOD]: 'CONNECT', 82 [HTTP2_HEADER_AUTHORITY]: `localhost:${port}`, 83 [HTTP2_HEADER_PATH]: '/' 84 }), 85 { 86 code: 'ERR_HTTP2_CONNECT_PATH', 87 message: 'The :path header is forbidden for CONNECT requests' 88 } 89 ); 90 91 // valid CONNECT request 92 const req = client.request({ 93 [HTTP2_HEADER_METHOD]: 'CONNECT', 94 [HTTP2_HEADER_AUTHORITY]: `localhost:${port}`, 95 }); 96 97 req.on('response', common.mustCall()); 98 let data = ''; 99 req.setEncoding('utf8'); 100 req.on('data', (chunk) => data += chunk); 101 req.on('end', common.mustCall(() => { 102 assert.strictEqual(data, 'hello'); 103 client.close(); 104 proxy.close(); 105 server.close(); 106 })); 107 req.end('hello'); 108 }); 109})); 110