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