'use strict'; const common = require('../common'); const fixtures = require('../common/fixtures'); if (!common.hasCrypto) common.skip('missing crypto'); const { strictEqual, ok } = require('assert'); const { createSecureContext } = require('tls'); const { createSecureServer, connect } = require('http2'); const { get } = require('https'); const { parse } = require('url'); const { connect: tls } = require('tls'); const countdown = (count, done) => () => --count === 0 && done(); const key = fixtures.readKey('agent8-key.pem'); const cert = fixtures.readKey('agent8-cert.pem'); const ca = fixtures.readKey('fake-startcom-root-cert.pem'); const clientOptions = { secureContext: createSecureContext({ ca }) }; function onRequest(request, response) { const { socket: { alpnProtocol } } = request.httpVersion === '2.0' ? request.stream.session : request; response.writeHead(200, { 'content-type': 'application/json' }); response.end(JSON.stringify({ alpnProtocol, httpVersion: request.httpVersion })); } function onSession(session, next) { const headers = { ':path': '/', ':method': 'GET', ':scheme': 'https', ':authority': `localhost:${this.server.address().port}` }; const request = session.request(headers); request.on('response', common.mustCall((headers) => { strictEqual(headers[':status'], 200); strictEqual(headers['content-type'], 'application/json'); })); request.setEncoding('utf8'); let raw = ''; request.on('data', (chunk) => { raw += chunk; }); request.on('end', common.mustCall(() => { const { alpnProtocol, httpVersion } = JSON.parse(raw); strictEqual(alpnProtocol, 'h2'); strictEqual(httpVersion, '2.0'); session.close(); this.cleanup(); if (typeof next === 'function') { next(); } })); request.end(); } // HTTP/2 & HTTP/1.1 server { const server = createSecureServer( { cert, key, allowHTTP1: true }, common.mustCall(onRequest, 2) ); server.listen(0); server.on('listening', common.mustCall(() => { const { port } = server.address(); const origin = `https://localhost:${port}`; const cleanup = countdown(2, () => server.close()); // HTTP/2 client connect( origin, clientOptions, common.mustCall(onSession.bind({ cleanup, server })) ); // HTTP/1.1 client get( Object.assign(parse(origin), clientOptions), common.mustCall((response) => { strictEqual(response.statusCode, 200); strictEqual(response.statusMessage, 'OK'); strictEqual(response.headers['content-type'], 'application/json'); response.setEncoding('utf8'); let raw = ''; response.on('data', (chunk) => { raw += chunk; }); response.on('end', common.mustCall(() => { const { alpnProtocol, httpVersion } = JSON.parse(raw); strictEqual(alpnProtocol, false); strictEqual(httpVersion, '1.1'); cleanup(); })); }) ); })); } // HTTP/2-only server { const server = createSecureServer( { cert, key }, common.mustCall(onRequest) ); server.once('unknownProtocol', common.mustCall((socket) => { socket.destroy(); })); server.listen(0); server.on('listening', common.mustCall(() => { const { port } = server.address(); const origin = `https://localhost:${port}`; const cleanup = countdown(3, () => server.close()); // HTTP/2 client connect( origin, clientOptions, common.mustCall(function(session) { onSession.call({ cleanup, server }, session, common.mustCall(testNoTls)); }) ); function testNoTls() { // HTTP/1.1 client get(Object.assign(parse(origin), clientOptions), common.mustNotCall) .on('error', common.mustCall(cleanup)) .on('error', common.mustCall(testWrongALPN)) .end(); } function testWrongALPN() { // Incompatible ALPN TLS client let text = ''; tls(Object.assign({ port, ALPNProtocols: ['fake'] }, clientOptions)) .setEncoding('utf8') .on('data', (chunk) => text += chunk) .on('end', common.mustCall(() => { ok(/Unknown ALPN Protocol, expected `h2` to be available/.test(text)); cleanup(); })); } })); }