• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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