• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3const common = require('../common');
4const fixtures = require('../common/fixtures');
5
6if (!common.hasCrypto)
7  common.skip('missing crypto');
8
9const { strictEqual, ok } = require('assert');
10const { createSecureContext } = require('tls');
11const { createSecureServer, connect } = require('http2');
12const { get } = require('https');
13const { parse } = require('url');
14const { connect: tls } = require('tls');
15const { Duplex } = require('stream');
16
17const countdown = (count, done) => () => --count === 0 && done();
18
19const key = fixtures.readKey('agent8-key.pem');
20const cert = fixtures.readKey('agent8-cert.pem');
21const ca = fixtures.readKey('fake-startcom-root-cert.pem');
22
23const clientOptions = { secureContext: createSecureContext({ ca }) };
24
25function onRequest(request, response) {
26  const { socket: { alpnProtocol } } = request.httpVersion === '2.0' ?
27    request.stream.session : request;
28  response.writeHead(200, { 'content-type': 'application/json' });
29  response.end(JSON.stringify({
30    alpnProtocol,
31    httpVersion: request.httpVersion
32  }));
33}
34
35function onSession(session, next) {
36  const headers = {
37    ':path': '/',
38    ':method': 'GET',
39    ':scheme': 'https',
40    ':authority': `localhost:${this.server.address().port}`
41  };
42
43  const request = session.request(headers);
44  request.on('response', common.mustCall((headers) => {
45    strictEqual(headers[':status'], 200);
46    strictEqual(headers['content-type'], 'application/json');
47  }));
48  request.setEncoding('utf8');
49  let raw = '';
50  request.on('data', (chunk) => { raw += chunk; });
51  request.on('end', common.mustCall(() => {
52    const { alpnProtocol, httpVersion } = JSON.parse(raw);
53    strictEqual(alpnProtocol, 'h2');
54    strictEqual(httpVersion, '2.0');
55
56    session.close();
57    this.cleanup();
58
59    if (typeof next === 'function') {
60      next();
61    }
62  }));
63  request.end();
64}
65
66// HTTP/2 & HTTP/1.1 server
67{
68  const server = createSecureServer(
69    { cert, key, allowHTTP1: true },
70    common.mustCall(onRequest, 2)
71  );
72
73  server.listen(0);
74
75  server.on('listening', common.mustCall(() => {
76    const { port } = server.address();
77    const origin = `https://localhost:${port}`;
78
79    const cleanup = countdown(2, () => server.close());
80
81    // HTTP/2 client
82    connect(
83      origin,
84      clientOptions,
85      common.mustCall(onSession.bind({ cleanup, server }))
86    );
87
88    // HTTP/1.1 client
89    get(
90      Object.assign(parse(origin), clientOptions),
91      common.mustCall((response) => {
92        strictEqual(response.statusCode, 200);
93        strictEqual(response.statusMessage, 'OK');
94        strictEqual(response.headers['content-type'], 'application/json');
95
96        response.setEncoding('utf8');
97        let raw = '';
98        response.on('data', (chunk) => { raw += chunk; });
99        response.on('end', common.mustCall(() => {
100          const { alpnProtocol, httpVersion } = JSON.parse(raw);
101          strictEqual(alpnProtocol, false);
102          strictEqual(httpVersion, '1.1');
103
104          cleanup();
105        }));
106      })
107    );
108  }));
109}
110
111// HTTP/2-only server
112{
113  const server = createSecureServer(
114    { cert, key },
115    common.mustCall(onRequest)
116  );
117
118  server.once('unknownProtocol', common.mustCall((socket) => {
119    strictEqual(socket instanceof Duplex, true);
120    socket.destroy();
121  }));
122
123  server.listen(0);
124
125  server.on('listening', common.mustCall(() => {
126    const { port } = server.address();
127    const origin = `https://localhost:${port}`;
128
129    const cleanup = countdown(3, () => server.close());
130
131    // HTTP/2 client
132    connect(
133      origin,
134      clientOptions,
135      common.mustCall(function(session) {
136        onSession.call({ cleanup, server },
137                       session,
138                       common.mustCall(testNoTls));
139      })
140    );
141
142    function testNoTls() {
143      // HTTP/1.1 client
144      get(Object.assign(parse(origin), clientOptions), common.mustNotCall())
145        .on('error', common.mustCall(cleanup))
146        .on('error', common.mustCall(testWrongALPN))
147        .end();
148    }
149
150    function testWrongALPN() {
151      // Incompatible ALPN TLS client
152      let text = '';
153      tls(Object.assign({ port, ALPNProtocols: ['fake'] }, clientOptions))
154        .setEncoding('utf8')
155        .on('data', (chunk) => text += chunk)
156        .on('end', common.mustCall(() => {
157          ok(/Unknown ALPN Protocol, expected `h2` to be available/.test(text));
158          cleanup();
159        }));
160    }
161  }));
162}
163